Commit bea75be0 by zhuzhequan

Merge branch 'dev' into 'master'

Dev

See merge request !58
parents a444ff77 4969e6d2
...@@ -71,7 +71,8 @@ axios.interceptors.response.use( ...@@ -71,7 +71,8 @@ axios.interceptors.response.use(
} }
return Promise.reject(res.data) return Promise.reject(res.data)
} else if (res.data.code === 500) { } else if (res.data.code === 500) {
Vue.prototype.$alert( Vue.prototype
.$alert(
'<div style="max-height:500px;overflow:auto">' + '<div style="max-height:500px;overflow:auto">' +
res.data.message + res.data.message +
'</div>', '</div>',
...@@ -80,6 +81,7 @@ axios.interceptors.response.use( ...@@ -80,6 +81,7 @@ axios.interceptors.response.use(
dangerouslyUseHTMLString: true dangerouslyUseHTMLString: true
} }
) )
.catch(() => {})
} }
} else { } else {
if (res.status === 400) { if (res.status === 400) {
......
<script> <script lang="jsx">
export default { export default {
name: 'CustomForm', name: 'CustomForm',
components: {}, components: {},
...@@ -17,7 +17,7 @@ export default { ...@@ -17,7 +17,7 @@ export default {
}, },
formLabelWidth: { formLabelWidth: {
type: String, type: String,
default: '50px' default: null
}, },
formItemWidth: { formItemWidth: {
type: String, type: String,
...@@ -95,7 +95,7 @@ export default { ...@@ -95,7 +95,7 @@ export default {
} }
}, },
created() { created() {
// console.log(104, this.formData) console.log(104, this.formLabelWidth)
}, },
render() { render() {
return ( return (
...@@ -124,6 +124,7 @@ export default { ...@@ -124,6 +124,7 @@ export default {
<el-input <el-input
v-model={this.formData[item.prop]} v-model={this.formData[item.prop]}
placeholder={item.placeholder || `请输入${item.name}`} placeholder={item.placeholder || `请输入${item.name}`}
show-password={item.password || false}
clearable></el-input> clearable></el-input>
)} )}
{item.type === 'textarea' && ( {item.type === 'textarea' && (
...@@ -131,17 +132,24 @@ export default { ...@@ -131,17 +132,24 @@ export default {
type="textarea" type="textarea"
v-model={this.formData[item.prop]} v-model={this.formData[item.prop]}
placeholder={item.placeholder || `请输入${item.name}`} placeholder={item.placeholder || `请输入${item.name}`}
maxlength={item.maxlength}
show-word-limit={!!item.maxlength}
clearable></el-input> clearable></el-input>
)} )}
{item.type === 'select' && ( {item.type === 'select' && (
<el-select <el-select
v-model={this.formData[item.prop]} v-model={this.formData[item.prop]}
{...{ attrs: item.attrs }}
onChange={(e) =>
item.events && item.events.onChange(this.formData, e)
}
placeholder={item.placeholder || `请选择${item.name}`} placeholder={item.placeholder || `请选择${item.name}`}
value-key={item.valueKey || 'value'}
clearable> clearable>
{item.options?.map((el, idx) => ( {item.options?.map((el, idx) => (
<el-option <el-option
label={el.label} label={el.label}
value={el.value} value={item.valueKey ? el : el.value}
key={idx}></el-option> key={idx}></el-option>
))} ))}
</el-select> </el-select>
...@@ -153,11 +161,13 @@ export default { ...@@ -153,11 +161,13 @@ export default {
placeholder="选择日期" placeholder="选择日期"
v-model={this.formData[item.prop]} v-model={this.formData[item.prop]}
clearable clearable
{...{ attrs: item.attrs }}
style="width: 100%;"></el-date-picker> style="width: 100%;"></el-date-picker>
)} )}
{item.type === 'radio' && {item.type === 'radio' &&
item.radioOptions?.map((el, idx) => ( item.radioOptions?.map((el, idx) => (
<el-radio <el-radio
{...{ attrs: item.attrs }}
v-model={this.formData[item.prop]} v-model={this.formData[item.prop]}
label={el.value} label={el.value}
key={idx}> key={idx}>
...@@ -167,7 +177,7 @@ export default { ...@@ -167,7 +177,7 @@ export default {
</el-form-item> </el-form-item>
))} ))}
{this.isCustomButton && ( {this.isCustomButton && (
<el-form-item> <el-form-item style="margin-right:5px">
<el-button <el-button
type="primary" type="primary"
onClick={this.search} onClick={this.search}
...@@ -179,7 +189,9 @@ export default { ...@@ -179,7 +189,9 @@ export default {
</el-button> </el-button>
</el-form-item> </el-form-item>
)} )}
{!this.isCustomButton && <slot name="btn"></slot>} {this.$slots.btn && (
<el-form-item> {this.$scopedSlots.btn()} </el-form-item>
)}
</el-form> </el-form>
) )
} }
......
...@@ -23,13 +23,9 @@ Vue.use(element) ...@@ -23,13 +23,9 @@ Vue.use(element)
Vue.use(vxeTable) Vue.use(vxeTable)
const clientWidth = document.body.clientWidth const clientWidth = document.body.clientWidth
const dpi = _.add( const dpi = _.add(_.ceil(_.divide(clientWidth / 1920)) + 0.5)
_.ceil(_.divide(clientWidth / 1920)) + 0.5,
)
let imagePath = '' let imagePath = ''
if ( if (md5(location.host) === '42f2b26039d7fd526b8a1d50bb329cbc') {
md5(location.host) === '42f2b26039d7fd526b8a1d50bb329cbc'
) {
localStorage.setItem('locationHost', location.host) localStorage.setItem('locationHost', location.host)
imagePath = zImgPath imagePath = zImgPath
} else if (zImgPath.indexOf('/erpimg') !== -1) { } else if (zImgPath.indexOf('/erpimg') !== -1) {
...@@ -41,14 +37,10 @@ Vue.prototype.setimgUrl = (url, options = {}) => { ...@@ -41,14 +37,10 @@ Vue.prototype.setimgUrl = (url, options = {}) => {
const { w, h, ...others } = options const { w, h, ...others } = options
let query = '' let query = ''
if (w) { if (w) {
query += query query += query ? '&w=' : 'w=' + _.floor(_.multiply(dpi * w))
? '&w='
: 'w=' + _.floor(_.multiply(dpi * w))
} }
if (h) { if (h) {
query += query query += query ? '&h=' : 'h=' + _.floor(_.multiply(dpi * h))
? '&h='
: 'h=' + _.floor(_.multiply(dpi * h))
} }
for (const key in others) { for (const key in others) {
query += `${query ? '&' : ''}${key}=${options[key]}` query += `${query ? '&' : ''}${key}=${options[key]}`
...@@ -70,6 +62,7 @@ router.afterEach((to, from) => { ...@@ -70,6 +62,7 @@ router.afterEach((to, from) => {
name: to.name, name: to.name,
path: to.fullPath, path: to.fullPath,
title: to.meta?.title || to.name, title: to.meta?.title || to.name,
meta: to.meta
}) })
store.commit('tags/setActive', to.name) store.commit('tags/setActive', to.name)
}) })
...@@ -87,5 +80,5 @@ router.beforeEach((to, form, next) => { ...@@ -87,5 +80,5 @@ router.beforeEach((to, form, next) => {
new Vue({ new Vue({
router, router,
store, store,
render: (h) => h(App), render: (h) => h(App)
}).$mount('#app') }).$mount('#app')
...@@ -79,6 +79,18 @@ const routes = [ ...@@ -79,6 +79,18 @@ const routes = [
meta: { title: '服务管理' } meta: { title: '服务管理' }
}, },
{ {
path: '/saas/sysSecuritySettings',
component: () => import('@/views/system/sysSecuritySettings.vue'),
name: 'system_security_settings',
meta: { title: '安全设置中心' }
},
{
path: '/saas/erpManagementList',
component: () => import('@/views/system/erpManagementList.vue'),
name: 'erpManagementList',
meta: { title: 'ERP启动列表' }
},
{
path: '/saas/countryCode', path: '/saas/countryCode',
component: () => import('@/views/system/countryCode.vue'), component: () => import('@/views/system/countryCode.vue'),
name: 'system_countryCode', name: 'system_countryCode',
...@@ -96,6 +108,7 @@ const routes = [ ...@@ -96,6 +108,7 @@ const routes = [
name: 'system_management', name: 'system_management',
meta: { title: '客户管理' } meta: { title: '客户管理' }
}, },
{ {
path: '/saas/user/recharge-record', path: '/saas/user/recharge-record',
component: () => import('@/views/user/rechargeRecord.vue'), component: () => import('@/views/user/rechargeRecord.vue'),
...@@ -151,6 +164,12 @@ const routes = [ ...@@ -151,6 +164,12 @@ const routes = [
meta: { title: '异常消息' } meta: { title: '异常消息' }
}, },
{ {
path: '/operation/asynchronousTask',
component: () => import('@/views/operation/asynchronousTask.vue'),
name: 'asynchronousTask',
meta: { title: '异步任务' }
},
{
path: '/saas/production/assistant/manage', path: '/saas/production/assistant/manage',
component: () => import('@/views/production/AssistantManage.vue'), component: () => import('@/views/production/AssistantManage.vue'),
name: 'production_assistant_manage', name: 'production_assistant_manage',
......
const lastSavedTags = localStorage.getItem('tags') ? JSON.parse(localStorage.getItem('tags')) || [] : [] const HOME_TAG = { name: 'home', path: '/saas/home', title: '首页', id: 0 }
let tagId = lastSavedTags.reduce( const lastSavedTags = localStorage.getItem('tags')
(a, c) => Math.max(a, c.id), ? JSON.parse(localStorage.getItem('tags')) || []
0, : []
) let tagId = lastSavedTags.reduce((a, c) => Math.max(a, c.id), 0)
// 确保首页始终在第一位
function ensureHomeFirst(tags) {
const filtered = tags.filter((t) => t.name !== 'home')
return [HOME_TAG, ...filtered]
}
console.log(lastSavedTags) console.log(lastSavedTags)
/** /**
* @type {import('vuex').StoreOptions} * @type {import('vuex').StoreOptions}
...@@ -10,32 +15,29 @@ console.log(lastSavedTags) ...@@ -10,32 +15,29 @@ console.log(lastSavedTags)
const tags = { const tags = {
namespaced: true, namespaced: true,
state: { state: {
tags: lastSavedTags || [], tags: ensureHomeFirst(lastSavedTags),
activeTag: undefined, activeTag: undefined
}, },
getters: { getters: {
currentTag(state) { currentTag(state) {
return state.tags.find( return state.tags.find((e) => e.name === state.activeTag)
(e) => e.name === state.activeTag, }
)
},
}, },
mutations: { mutations: {
add(state, tag) { add(state, tag) {
// 禁止添加重复首页tag
if (tag.name === 'home') return HOME_TAG.id
const item = { const item = {
...tag, ...tag,
id: ++tagId, id: ++tagId
} }
const index = state.tags.findIndex( const index = state.tags.findIndex((t) => t.name === tag.name)
(t) => t.name === tag.name,
)
if (index === -1) { if (index === -1) {
state.tags.push(item) state.tags.push(item)
} }
localStorage.setItem( // 始终保证首页在第一位
'tags', state.tags = ensureHomeFirst(state.tags)
JSON.stringify(state.tags), localStorage.setItem('tags', JSON.stringify(state.tags))
)
return item.id return item.id
}, },
setActive(state, name) { setActive(state, name) {
...@@ -43,9 +45,9 @@ const tags = { ...@@ -43,9 +45,9 @@ const tags = {
state.activeTag = name state.activeTag = name
}, },
remove(state, name) { remove(state, name) {
const index = state.tags.findIndex( // 禁止移除首页tag
(e) => e.name === name, if (name === 'home') return
) const index = state.tags.findIndex((e) => e.name === name)
if (index === -1) return if (index === -1) return
state.tags.splice(index, 1) state.tags.splice(index, 1)
...@@ -60,15 +62,12 @@ const tags = { ...@@ -60,15 +62,12 @@ const tags = {
} }
} }
localStorage.setItem( localStorage.setItem('tags', JSON.stringify(state.tags))
'tags',
JSON.stringify(state.tags),
)
}, },
removeAllTags(state) { removeAllTags(state) {
state.tags = [] state.tags = []
}, }
}, }
} }
export default tags export default tags
...@@ -22,18 +22,29 @@ export default { ...@@ -22,18 +22,29 @@ export default {
name: 'HomeView', name: 'HomeView',
components: { components: {
NavMenu, NavMenu,
HomeHeader, HomeHeader
}, },
computed: { computed: {
...mapState('tags', ['tags']), ...mapState('tags', ['tags']),
cacheView() { cacheView() {
// 在router中meta.keepAlive设置为true才能开启缓存
const arr = [] const arr = []
return arr.concat(this.tags.map((item) => item.name)) return arr.concat(
}, this.tags
.map((item) => {
if (item.meta && item.meta.keepAlive) {
return item.name
}
return null
})
.filter((el) => el !== null)
)
}
}, },
data() { data() {
return {} return {}
}, }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
......
...@@ -229,55 +229,7 @@ export default { ...@@ -229,55 +229,7 @@ export default {
index: '/saas/taskManage', index: '/saas/taskManage',
children: [] children: []
}, },
{
id: 2,
path: '',
label: '系统管理',
icon: 'el-icon-setting',
index: '/system',
children: [
{
id: 1,
path: '',
label: '用户管理',
icon: 'el-icon-s-order',
index: 'user',
children: []
},
{
id: 2,
path: '',
label: '角色管理',
icon: 'el-icon-s-order',
index: 'user',
children: []
},
{
id: 3,
path: '',
label: '定时任务',
icon: 'el-icon-message-solid',
index: '/saas/timed_task',
children: []
},
{
id: 4,
path: '',
label: '公告管理',
icon: 'el-icon-s-promotion',
index: '/saas/announceManage',
children: []
},
{
id: 5,
path: '',
label: '服务管理',
icon: 'el-icon-s-order',
index: '/saas/services',
children: []
}
]
},
{ {
id: 5, id: 5,
path: '', path: '',
...@@ -384,6 +336,14 @@ export default { ...@@ -384,6 +336,14 @@ export default {
children: [] children: []
}, },
{ {
id: 6_2,
path: '',
label: '异步任务',
icon: 'el-icon-postcard',
index: '/operation/asynchronousTask',
children: []
},
{
id: 9, id: 9,
path: '', path: '',
label: 'sql执行工具', label: 'sql执行工具',
...@@ -433,6 +393,63 @@ export default { ...@@ -433,6 +393,63 @@ export default {
icon: 'el-icon-suitcase-1', icon: 'el-icon-suitcase-1',
index: '/saas/production/assistant/manage', index: '/saas/production/assistant/manage',
children: [] children: []
},
{
id: 9,
path: '',
label: '系统管理',
icon: 'el-icon-setting',
index: '/system',
children: [
{
id: 1,
path: '',
label: '用户管理',
icon: 'el-icon-s-order',
index: 'user',
children: []
},
{
id: 2,
path: '',
label: '角色管理',
icon: 'el-icon-s-order',
index: 'user',
children: []
},
{
id: 3,
path: '',
label: '定时任务',
icon: 'el-icon-message-solid',
index: '/saas/timed_task',
children: []
},
{
id: 4,
path: '',
label: '公告管理',
icon: 'el-icon-s-promotion',
index: '/saas/announceManage',
children: []
},
{
id: 5,
path: '',
label: '服务管理',
icon: 'el-icon-s-order',
index: '/saas/services',
children: []
},
{
id: 7,
path: '',
label: '安全设置中心',
icon: 'el-icon-lock',
index: '/saas/sysSecuritySettings',
children: []
}
]
} }
], ],
msg_list: [], msg_list: [],
......
<template>
<div class="recharge-record card">
<div class="search">
<CustomForm
:formConfig="queryformConfig"
v-model="queryFormData"
:isCustomButton="false"
:isFlex="false">
<template slot="btn">
<el-button type="primary" icon="el-icon-search" @click="search">
查询
</el-button>
</template>
</CustomForm>
</div>
<div class="table_wrap" v-loading="loading">
<table-vue
:sourceData="sourceData"
ref="multipleTable"
:tableColumns="usersTableColumns"></table-vue>
</div>
<div class="pagination">
<el-pagination
layout="sizes, total, prev, pager, next, jumper"
background
:total="paginationOptions.total"
:page-size="paginationOptions.pageSize"
:current-page="paginationOptions.currentPage"
@size-change="sizeChange"
@current-change="onCurrentChange"></el-pagination>
</div>
</div>
</template>
<script>
import axios from '../../common/api/axios'
import CustomForm from '@/common/components/base/CustomForm.vue'
import { mapState } from 'vuex'
import tableVue from '@/common/components/base/tableView.vue'
export default {
name: 'asynchronousTask',
components: {
tableVue,
CustomForm
},
data() {
return {
is_title: 1,
ishowForm: false,
select: '',
sourceData: [],
serviceNameList: { sysService: '系统服务同步' },
formData: {},
queryFormData: {},
queryformConfig: [
{
prop: 'timeRange',
type: 'datePicker',
dateType: 'datetimerange',
name: ' 提交时间',
attrs: {
'start-placeholder': '开始时间',
'end-placeholder': '结束时间',
'value-format': 'yyyy-MM-dd HH:mm:ss',
'default-time': ['00:00:00', '23:59:59']
}
},
{
prop: 'runStatus',
type: 'select',
name: '状态',
options: [
{ label: '进行中', value: 0 },
{ label: '完成', value: 1 },
{ label: '失败', value: 2 }
]
}
],
dialogVisible: false,
formId: null,
paginationOptions: {
pageSize: 100,
currentPage: 1,
total: 0
},
details: [],
loading: false
}
},
async created() {
// await this.getServiceNameList()
this.getList()
},
computed: {
...mapState(['reqMenu', 'employee']),
usersTableColumns() {
return [
{
label: '任务类型',
key: 'taskType',
render: (item) => <span>{this.serviceNameList[item.taskType]}</span>
},
{
label: '任务标题',
key: 'taskTitle',
width: ''
},
{
label: '异常信息',
key: 'exceptionMsg',
width: ''
},
{
label: '状态',
key: 'runStatus',
width: '',
render: (item) => (
<span class={['text', this.getColor(item.runStatus)]}>
{item.runStatus === 0
? '进行中'
: item.runStatus === 1
? '完成'
: '失败'}
</span>
)
},
{
label: '操作人',
key: 'operatorName',
width: '100'
},
{
label: '提交时间',
key: 'submitTime',
width: ''
},
{
label: '开始时间',
key: 'startTime',
width: ''
},
{
label: '结束时间',
key: 'endTime',
width: '100'
},
{
label: '相关操作',
key: '',
fixed: 'right',
width: '130',
render: (item) =>
(item.runStatus !== 1 && (
<div>
<span
title="重新发送"
class="icon-view resendMsg"
onClick={() => this.resendMsg(item.id)}>
<i class="el-icon-s-promotion"></i>
</span>
</div>
)) ||
''
}
]
}
},
methods: {
sizeChange(value) {
this.paginationOptions.pageSize = value
this.getList()
},
onCurrentChange(value) {
this.paginationOptions.currentPage = value
this.getList()
},
selectionChange(selection) {
if (selection.length > 0) {
this.select = selection
}
},
search() {
if (this.queryFormData.timeRange && this.queryFormData.timeRange.length) {
this.queryFormData.minSubmitTime = this.queryFormData.timeRange[0]
this.queryFormData.maxSubmitTime = this.queryFormData.timeRange[1]
}
this.getList()
},
// 查询
getList() {
this.loading = true
const { pageSize, currentPage } = this.paginationOptions
axios
.post('sys/syncTaskLog/list_page ', {
pageSize,
currentPage,
...this.queryFormData
})
.then((res) => {
if (res.code === 200) {
this.sourceData = res.data.records
this.paginationOptions.total = res.data.total
} else {
this.$alert(res.message, '错误提示', {
dangerouslyUseHTMLString: true
})
}
this.loading = false
})
},
async resendMsg(id) {
const params = { id }
try {
this.$confirm('确认发送?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(async () => {
await axios.get('sys/syncTaskLog/retry_send', { params })
this.$message.success('发送成功')
this.getList()
})
.catch(() => {
this.$message.warning('取消发送')
})
} catch (error) {
console.log(error)
this.$message.warning(error.message)
} finally {
this.loading = false
}
},
getColor(status) {
if (status === 0) {
return 'afootStatus'
} else if (status === 1) {
return 'successStatus'
} else {
return 'failStatus'
}
}
}
}
</script>
<style scoped lang="less">
.wraper {
display: flex;
flex-direction: column;
height: 100%;
}
.table_wrap {
flex: 1;
padding: 0;
}
.circle {
display: inline-block;
height: 10px;
width: 10px;
border-radius: 5px;
margin-right: 5px;
}
.my-table .first {
background-color: red !important;
color: #fff !important;
}
::v-deep.switchStyle .el-switch__label {
position: absolute;
display: none;
color: #fff;
span {
font-size: 12px !important;
}
}
.el-switch__core {
background-color: rgba(166, 166, 166, 1);
}
::v-deep.switchStyle .el-switch__label--left {
z-index: 9;
left: 20px;
}
::v-deep.switchStyle .el-switch__label--right {
z-index: 9;
right: 20px;
}
::v-deep.switchStyle .el-switch__label.is-active {
display: block;
}
::v-deep.switchStyle.el-switch .el-switch__core,
.el-switch .el-switch__label {
width: 50px !important;
}
.resendMsg {
background-color: #ff9900;
&:hover {
background-color: #ffd36a;
}
}
.text {
color: #fff;
border-radius: 4px;
padding: 1px 5px;
}
.afootStatus {
background-color: #409eff;
}
.successStatus {
background-color: #67c23a;
}
.failStatus {
background-color: #f56c6c;
}
.recharge-record {
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
&::v-deep {
.el-table .el-table__cell {
padding: 6px 0;
}
.el-input__inner {
padding: 0 4px;
}
.el-input--small .el-input__inner {
height: 30px;
line-height: 30px;
}
.el-icon-time:before {
content: '';
}
.el-dialog__footer {
text-align: center;
.el-button--small {
padding: 9px 50px;
}
}
.el-dialog__body {
padding: 10px 20px;
}
.el-icon-edit:before {
color: #ff9800;
font-weight: bold;
}
.el-range-editor--small .el-range-separator {
line-height: 31px;
}
.header-row-class-name th {
background-color: #f8f8f9;
}
.el-input-group__append {
padding-left: 0;
}
}
}
</style>
<template>
<div class="recharge-record card">
<div class="search">
<CustomForm
:formConfig="queryformConfig"
v-model="queryFormData"
:is-custom-button="false"
:isFlex="false">
<template slot="btn">
<el-button type="primary" icon="el-icon-search" @click="search">
查询
</el-button>
</template>
</CustomForm>
</div>
<div class="table_wrap" v-loading="loading">
<table-vue
:sourceData="sourceData"
ref="multipleTable"
:tableColumns="usersTableColumns"
@currentChange="currentTabFn"
@selectionChange="selectionChange"></table-vue>
</div>
<div class="pagination">
<el-pagination
layout="sizes, total, prev, pager, next, jumper"
background
:total="paginationOptions.total"
:page-size="paginationOptions.pageSize"
:current-page="paginationOptions.currentPage"
@size-change="sizeChange"
@current-change="onCurrentChange"></el-pagination>
</div>
</div>
</template>
<script>
import axios from '../../common/api/axios'
import CustomForm from '@/common/components/base/CustomForm.vue'
import { mapState } from 'vuex'
import tableVue from '@/common/components/base/tableView.vue'
export default {
name: 'erpManagementList',
components: {
tableVue,
CustomForm
},
data() {
return {
is_title: 1,
ishowForm: false,
select: '',
sourceData: [],
serviceNameList: new Map(),
formData: {},
queryFormData: {},
queryformConfig: [
{
prop: 'name',
type: 'select',
name: '名称',
options: []
}
],
dialogVisible: false,
formId: null,
paginationOptions: {
pageSize: 100,
currentPage: 1,
total: 0
},
details: [],
loading: false
}
},
async created() {
await this.getServiceNameList()
this.getList()
},
computed: {
...mapState(['reqMenu', 'employee']),
usersTableColumns() {
return [
{
label: 'namespace',
key: 'namespace'
},
{
label: '服务名称',
key: 'name',
render: (item) => (
<span>{this.serviceNameList.get(item.name).label}</span>
)
},
{
label: '服务类型',
key: 'type',
render: (item) => (
<span>{item.type === 'basics' ? '基础' : '增值'}</span>
)
},
{
label: '收费标准',
key: 'rates'
},
{
label: '状态',
key: 'enable',
width: '100',
render: (item) => <span>{item.enable ? '启用' : '停用'}</span>
},
{
label: '是否参与折扣',
key: 'discount',
width: '100',
render: (item) => <span>{item.discount === true ? '是' : '否'}</span>
}
]
},
funcRoleList() {
if (this.roleList.length > 0) {
return this.roleList.filter((item) => item.type === 'FUNCTION_ROLE')
}
return []
},
dataRoleList() {
if (this.roleList.length > 0) {
return this.roleList.filter((item) => item.type === 'DATA_ROLE')
}
return []
}
},
watch: {
formData: {
handler(newValue) {
// console.log(368, newValue)
},
immediate: true,
deep: true
}
},
methods: {
sizeChange(value) {
this.paginationOptions.pageSize = value
this.getList()
},
onCurrentChange(value) {
this.paginationOptions.currentPage = value
this.getList()
},
selectionChange(selection) {
if (selection.length > 0) {
this.select = selection
}
},
setempNo(id) {
for (const iterator of this.employee) {
if (iterator.id === id) {
this.addcurrencyform.empNumber = iterator.empNumber
break
}
}
},
search() {
this.getList()
},
currentTabFn(val) {
if (val.row) {
this.formId = val.row.id
}
},
// 修改新增
async addDialog(id, e) {
e && e.stopPropagation()
this.ishowForm = true
try {
if (id) {
this.is_title = 2
const url = '/serviceManagement/get'
const res = await axios.get(url, { params: { id } })
this.formData = { ...res.data }
this.formData.nameObj = this.serviceNameList.get(this.formData.name)
this.formData.serveType =
this.serviceNameList.get(this.formData.name).type === 'basics'
? '基础'
: '增值'
console.log(289, this.formData)
} else {
this.is_title = 1
}
this.dialogVisible = true
} catch (error) {}
},
async checkData() {
const [isValid, postData] = await Promise.all([
new Promise((resolve) => {
this.$refs.formRefs
.validateForm()
.then((res) => resolve(true))
.catch((err) => {
resolve(false)
console.log(err)
})
}),
new Promise((resolve) => {
const params = {
name: '',
id: '',
type: '',
tollCollectionManner: '',
rates: '',
enable: '',
discount: '',
remarks: ''
}
for (const key in params) {
params[key] = this.formData[key]
}
resolve(params)
})
])
console.log(isValid, postData)
return { isValid, postData }
},
// 新增
async addServiceManagement() {
const isAdd = this.is_title === 1
const url = isAdd ? '/serviceManagement/add' : '/serviceManagement/update'
try {
const { isValid, postData } = await this.checkData()
if (isValid) {
console.log('add', this.formData)
const finalData = isAdd ? { ...postData, id: undefined } : postData
const res = await axios.post(url, finalData)
if (res.code !== 200) {
return
}
this.dialogVisible = false
this.getList()
this.$message.success(isAdd ? '新增成功' : '更新成功')
} else {
console.log()
}
} catch (error) {
console.log(error)
}
},
// 查询
getList() {
this.loading = true
const { pageSize, currentPage } = this.paginationOptions
axios
.post('serviceManagementLog/list_page', {
pageSize,
currentPage,
...this.queryFormData
})
.then((res) => {
if (res.code === 200) {
this.sourceData = res.data.records
this.paginationOptions.total = res.data.total
} else {
this.$alert(res.message, '错误提示', {
dangerouslyUseHTMLString: true
})
}
this.loading = false
})
},
// 删除
deleteSection(data, e) {
e && e.stopPropagation()
const ids = [data.id].join()
this.$confirm('确定删除选中的信息?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
axios
.get('serviceManagement/delete', {
params: {
ids
}
})
.then((res) => {
if (res.code === 200) {
this.$message({
type: 'success',
message: '删除成功!'
})
this.getList()
}
})
})
.catch(() => {})
},
async closedFn() {
this.dialogVisible = false
this.formData = { enable: true, discount: true }
await this.$refs.formRefs?.resetFields()
},
async getServiceNameList() {
try {
const { data } = await axios.get('serviceManagement/serviceNameList')
const newData = data.map((el) => {
return {
label: el.remark,
value: el.code,
type: el.type
}
})
this.serviceNameList = new Map(
newData.map((item) => [
item.value,
{ label: item.label, type: item.type, value: item.value }
])
)
this.queryformConfig[0].options = [...newData]
console.log(509, this.serviceNameList)
} catch (error) {
console.log(error)
}
},
async enableChange(item, value) {
this.loading = true
try {
const params = {
id: item.id,
status: value
}
console.log(item, value)
await axios.get('/serviceManagement/updateStatus', { params })
this.getList()
} catch (error) {
console.log(error)
}
},
async updateListFn() {
this.loading = true
try {
await axios.get('serviceManagement/syncErp')
} catch (error) {
console.log(error)
this.$message.warning(error.message)
} finally {
this.loading = false
}
},
toList() {}
}
}
</script>
<style scoped lang="less">
.recharge-record {
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
&::v-deep {
.el-table .el-table__cell {
padding: 6px 0;
}
.el-input__inner {
padding: 0 4px;
}
.el-input--small .el-input__inner {
height: 30px;
line-height: 30px;
}
.el-icon-time:before {
content: '';
}
.el-dialog__footer {
text-align: center;
.el-button--small {
padding: 9px 50px;
}
}
.el-dialog__body {
padding: 10px 20px;
}
.el-icon-edit:before {
color: #ff9800;
font-weight: bold;
}
.el-range-editor--small .el-range-separator {
line-height: 31px;
}
.header-row-class-name th {
background-color: #f8f8f9;
}
.el-input-group__append {
padding-left: 0;
}
}
}
.wraper {
display: flex;
flex-direction: column;
height: 100%;
}
.table_wrap {
flex: 1;
padding: 0;
}
.circle {
display: inline-block;
height: 10px;
width: 10px;
border-radius: 5px;
margin-right: 5px;
}
.my-table .first {
background-color: red !important;
color: #fff !important;
}
::v-deep.switchStyle .el-switch__label {
position: absolute;
display: none;
color: #fff;
span {
font-size: 12px !important;
}
}
.el-switch__core {
background-color: rgba(166, 166, 166, 1);
}
::v-deep.switchStyle .el-switch__label--left {
z-index: 9;
left: 20px;
}
::v-deep.switchStyle .el-switch__label--right {
z-index: 9;
right: 20px;
}
::v-deep.switchStyle .el-switch__label.is-active {
display: block;
}
::v-deep.switchStyle.el-switch .el-switch__core,
.el-switch .el-switch__label {
width: 50px !important;
}
</style>
<template> <template>
<div class="wraper"> <div class="recharge-record card">
<div class="search">
<CustomForm <CustomForm
:formConfig="queryformConfig" :formConfig="queryformConfig"
v-model="queryFormData" v-model="queryFormData"
@addDialog="addDialog" @addDialog="addDialog"
@searchFn="search" @searchFn="search"
:isFlex="false"></CustomForm> :isFlex="false">
<template slot="btn">
<el-button type="primary" @click="updateListFn">同步</el-button>
<el-button type="primary" @click="toList">查看ERP启动列表</el-button>
</template>
</CustomForm>
</div>
<div class="table_wrap" v-loading="loading"> <div class="table_wrap" v-loading="loading">
<table-vue <table-vue
:sourceData="sourceData" :sourceData="sourceData"
...@@ -24,7 +31,6 @@ ...@@ -24,7 +31,6 @@
@size-change="sizeChange" @size-change="sizeChange"
@current-change="onCurrentChange"></el-pagination> @current-change="onCurrentChange"></el-pagination>
</div> </div>
<!-- 弹出层 --> <!-- 弹出层 -->
<el-dialog <el-dialog
:close-on-click-modal="false" :close-on-click-modal="false"
...@@ -62,8 +68,9 @@ import axios from '../../common/api/axios' ...@@ -62,8 +68,9 @@ import axios from '../../common/api/axios'
import CustomForm from '@/common/components/base/CustomForm.vue' import CustomForm from '@/common/components/base/CustomForm.vue'
import { mapState } from 'vuex' import { mapState } from 'vuex'
import tableVue from '@/common/components/base/tableView.vue' import tableVue from '@/common/components/base/tableView.vue'
export default { export default {
name: 'system_users', name: 'system_services',
components: { components: {
tableVue, tableVue,
CustomForm CustomForm
...@@ -74,129 +81,11 @@ export default { ...@@ -74,129 +81,11 @@ export default {
ishowForm: false, ishowForm: false,
select: '', select: '',
sourceData: [], sourceData: [],
serviceNameList: [], serviceNameList: new Map(),
newServiceNameList: [],
formData: {}, formData: {},
queryFormData: {}, queryFormData: {},
queryformConfig: [
{
prop: 'name',
type: 'select',
name: '名称',
options: []
},
{
prop: 'enable',
type: 'select',
name: '状态',
options: [
{ label: '启用', value: true },
{ label: '停用', value: false }
]
}
],
editformConfig: [
{
prop: 'name',
type: 'select',
name: '名称',
options: [],
renderRules: (item) => [
{
required: true,
message: '请选择名称',
trigger: 'change'
}
]
},
{
prop: 'type',
type: 'select',
name: '服务类型',
options: [
{ label: '基础', value: 'basics' },
{ label: '增值', value: 'appreciation' }
],
renderRules: (item) => [
{
required: true,
message: '请选择服务类型',
trigger: 'change'
}
]
},
{
prop: 'tollCollectionManner',
type: 'select',
name: '收费方式',
options: [
{ label: '按单', value: 'order' },
{ label: '按月', value: 'monthly' },
{ label: '免费', value: '免费' }
],
renderRules: (item) => [
{
required: true,
message: '请选择收费方式',
trigger: 'change'
}
]
},
{
prop: 'rates',
type: 'input',
name: '收费标准',
renderRules: (item) => [
{
required: item.tollCollectionManner !== '免费',
validator: (rule, value, callback) => {
if (item.tollCollectionManner !== '免费') {
if (value === '' || value === null || value === undefined) {
return callback(new Error('请输入收费标准'))
}
}
const numValue = Number(value)
if (isNaN(numValue)) {
return callback(new Error('请输入有效数字'))
}
if (numValue < 0) {
return callback(new Error('数值必须大于等于0'))
}
// 校验通过
callback()
},
trigger: 'blur'
}
]
},
{
prop: 'enable',
type: 'radio',
name: '服务启用状态',
defaultValue: true,
radioOptions: [
{ label: '启用', value: true },
{ label: '停用', value: false }
]
},
{
prop: 'discount',
type: 'radio',
name: '参与折扣',
defaultValue: true,
radioOptions: [
{ label: '是', value: true },
{ label: '否', value: false }
]
},
{
prop: 'remarks',
type: 'textarea',
name: '备注'
}
],
dialogVisible: false, dialogVisible: false,
formId: null, formId: null,
...@@ -209,8 +98,8 @@ export default { ...@@ -209,8 +98,8 @@ export default {
loading: false loading: false
} }
}, },
created() { async created() {
this.getServiceNameList() await this.getServiceNameList()
this.getList() this.getList()
}, },
...@@ -222,7 +111,9 @@ export default { ...@@ -222,7 +111,9 @@ export default {
label: '服务名称', label: '服务名称',
key: 'name', key: 'name',
width: '', width: '',
render: (item) => <span>{this.serviceNameList.get(item.name)}</span> render: (item) => (
<span>{this.serviceNameList.get(item.name).label}</span>
)
}, },
{ {
label: '服务类型', label: '服务类型',
...@@ -234,7 +125,7 @@ export default { ...@@ -234,7 +125,7 @@ export default {
) )
}, },
{ {
label: '收费方式', label: '收费方式(¥)',
key: 'tollCollectionManner', key: 'tollCollectionManner',
width: '', width: '',
...@@ -259,7 +150,16 @@ export default { ...@@ -259,7 +150,16 @@ export default {
width: '100', width: '100',
render: (item) => ( render: (item) => (
<span>{item.enable === true ? '启用' : '停用'}</span> <span>
<el-switch
class="switchStyle"
v-model={item.enable}
active-color="#13ce66"
inactive-color="#ff4949"
active-text="启用"
inactive-text="停用"
onChange={(e) => this.enableChange(item, e)}></el-switch>
</span>
) )
}, },
{ {
...@@ -309,7 +209,137 @@ export default { ...@@ -309,7 +209,137 @@ export default {
} }
] ]
}, },
queryformConfig() {
return [
{
prop: 'name',
type: 'select',
name: '名称',
options: [...(this.newServiceNameList || [])]
},
{
prop: 'type',
type: 'select',
name: '服务类型',
options: [
{ label: '基础', value: 'basics' },
{ label: '增值', value: 'appreciation' }
]
},
{
prop: 'enable',
type: 'select',
name: '状态',
options: [
{ label: '启用', value: true },
{ label: '停用', value: false }
]
}
]
},
editformConfig() {
return [
{
prop: 'nameObj',
type: 'select',
name: '名称',
options: [...(this.newServiceNameList || [])],
valueKey: 'value',
events: {
onChange: (item, value) => {
item.name = value.value
item.serveType = value.type === 'basics' ? '基础' : '增值'
item.type = value.type
}
},
renderRules: () => [
{
required: true,
message: '请选择名称',
trigger: 'change'
}
]
},
{
prop: 'serveType',
type: 'input',
name: '服务类型',
attrs: { disabled: true, placeholder: '选择完名称后展示' }
},
{
prop: 'tollCollectionManner',
type: 'select',
name: '收费方式',
options: [
{ label: '按单', value: 'order' },
{ label: '按月', value: 'monthly' },
{ label: '免费', value: '免费' }
],
renderRules: (item) => [
{
required: true,
message: '请选择收费方式',
trigger: 'change'
}
]
},
{
prop: 'rates',
type: 'input',
name: '收费标准',
renderRules: (item) => [
{
required: item.tollCollectionManner !== '免费',
validator: (rule, value, callback) => {
if (item.tollCollectionManner !== '免费') {
if (value === '' || value === null || value === undefined) {
return callback(new Error('请输入收费标准'))
}
}
const numValue = Number(value)
if (isNaN(numValue)) {
return callback(new Error('请输入有效数字'))
}
if (numValue < 0) {
return callback(new Error('数值必须大于等于0'))
}
// 校验通过
callback()
},
trigger: 'blur'
}
]
},
{
prop: 'enable',
type: 'radio',
name: '服务启用状态',
defaultValue: true,
radioOptions: [
{ label: '启用', value: true },
{ label: '停用', value: false }
]
},
{
prop: 'discount',
type: 'radio',
name: '参与折扣',
defaultValue: true,
radioOptions: [
{ label: '是', value: true },
{ label: '否', value: false }
]
},
{
prop: 'remarks',
type: 'textarea',
name: '备注'
}
]
},
funcRoleList() { funcRoleList() {
if (this.roleList.length > 0) { if (this.roleList.length > 0) {
return this.roleList.filter((item) => item.type === 'FUNCTION_ROLE') return this.roleList.filter((item) => item.type === 'FUNCTION_ROLE')
...@@ -323,15 +353,7 @@ export default { ...@@ -323,15 +353,7 @@ export default {
return [] return []
} }
}, },
watch: {
formData: {
handler(newValue) {
// console.log(368, newValue)
},
immediate: true,
deep: true
}
},
methods: { methods: {
sizeChange(value) { sizeChange(value) {
this.paginationOptions.pageSize = value this.paginationOptions.pageSize = value
...@@ -374,6 +396,12 @@ export default { ...@@ -374,6 +396,12 @@ export default {
const res = await axios.get(url, { params: { id } }) const res = await axios.get(url, { params: { id } })
this.formData = { ...res.data } this.formData = { ...res.data }
this.formData.nameObj = this.serviceNameList.get(this.formData.name)
this.formData.serveType =
this.serviceNameList.get(this.formData.name).type === 'basics'
? '基础'
: '增值'
console.log(289, this.formData)
} else { } else {
this.is_title = 1 this.is_title = 1
} }
...@@ -448,7 +476,6 @@ export default { ...@@ -448,7 +476,6 @@ export default {
...this.queryFormData ...this.queryFormData
}) })
.then((res) => { .then((res) => {
this.loading = false
if (res.code === 200) { if (res.code === 200) {
this.sourceData = res.data.records this.sourceData = res.data.records
this.paginationOptions.total = res.data.total this.paginationOptions.total = res.data.total
...@@ -457,6 +484,7 @@ export default { ...@@ -457,6 +484,7 @@ export default {
dangerouslyUseHTMLString: true dangerouslyUseHTMLString: true
}) })
} }
this.loading = false
}) })
}, },
...@@ -497,29 +525,110 @@ export default { ...@@ -497,29 +525,110 @@ export default {
async getServiceNameList() { async getServiceNameList() {
try { try {
const { data } = await axios.get('serviceManagement/serviceNameList') const { data } = await axios.get('serviceManagement/serviceNameList')
const newData = data.map((el) => { this.newServiceNameList = data.map((el) => {
return { return {
label: el.value, label: el.remark,
value: el.key value: el.code,
type: el.type
} }
}) })
this.serviceNameList = new Map( this.serviceNameList = new Map(
newData.map((item) => [item.value, item.label]) this.newServiceNameList.map((item) => [
item.value,
{ label: item.label, type: item.type, value: item.value }
])
) )
console.log(509, this.serviceNameList) } catch (error) {
console.log(error)
}
},
async enableChange(item, value) {
this.loading = true
try {
const params = {
id: item.id,
status: value
}
console.log(item, value)
this.queryformConfig[0].options = [...newData] await axios.get('/serviceManagement/updateStatus', { params })
this.editformConfig[0].options = [...newData] this.getList()
} catch (error) { } catch (error) {
this.queryformConfig[0].options = []
this.editformConfig[0].options = []
console.log(error) console.log(error)
} }
},
async updateListFn() {
this.loading = true
try {
await axios.get('serviceManagement/syncErp')
} catch (error) {
console.log(error)
this.$message.warning(error.message)
} finally {
this.loading = false
}
},
toList() {
this.$router.push('/saas/erpManagementList')
} }
} }
} }
</script> </script>
<style scoped> <style scoped lang="less">
.recharge-record {
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
&::v-deep {
.el-table .el-table__cell {
padding: 6px 0;
}
.el-input__inner {
padding: 0 4px;
}
.el-input--small .el-input__inner {
height: 30px;
line-height: 30px;
}
.el-icon-time:before {
content: '';
}
.el-dialog__footer {
text-align: center;
.el-button--small {
padding: 9px 50px;
}
}
.el-dialog__body {
padding: 10px 20px;
}
.el-icon-edit:before {
color: #ff9800;
font-weight: bold;
}
.el-range-editor--small .el-range-separator {
line-height: 31px;
}
.header-row-class-name th {
background-color: #f8f8f9;
}
.el-input-group__append {
padding-left: 0;
}
}
}
.wraper { .wraper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -528,6 +637,7 @@ export default { ...@@ -528,6 +637,7 @@ export default {
.table_wrap { .table_wrap {
flex: 1; flex: 1;
padding: 0;
} }
.circle { .circle {
...@@ -538,8 +648,34 @@ export default { ...@@ -538,8 +648,34 @@ export default {
margin-right: 5px; margin-right: 5px;
} }
.my-table >>> .first { .my-table .first {
background-color: red !important; background-color: red !important;
color: #fff !important; color: #fff !important;
} }
::v-deep.switchStyle .el-switch__label {
position: absolute;
display: none;
color: #fff;
span {
font-size: 12px !important;
}
}
.el-switch__core {
background-color: rgba(166, 166, 166, 1);
}
::v-deep.switchStyle .el-switch__label--left {
z-index: 9;
left: 20px;
}
::v-deep.switchStyle .el-switch__label--right {
z-index: 9;
right: 20px;
}
::v-deep.switchStyle .el-switch__label.is-active {
display: block;
}
::v-deep.switchStyle.el-switch .el-switch__core,
.el-switch .el-switch__label {
width: 50px !important;
}
</style> </style>
<template>
<div class="wraper" v-loading="loading">
<div v-if="!loading" class="card-content">
<el-row class="input-group" align="middle" :gutter="20">
<el-col :span="6" class="input-label">手机号</el-col>
<el-col :span="12" class="input-value">{{ securityData.phone }}</el-col>
<el-col :span="6" class="action">
<span v-if="securityData.phone">
<el-button type="text" @click="openDialog('phone', 'unbind')">
解绑
</el-button>
<el-button type="text" @click="openDialog('phone', 'change')">
换绑
</el-button>
</span>
<span v-else>
<el-button
type="text"
style="color: orange"
@click="openDialog('phone', 'bind')">
绑定手机号
</el-button>
</span>
</el-col>
</el-row>
<el-divider />
<el-row class="input-group" align="middle" :gutter="20">
<el-col :span="6" class="input-label">邮箱</el-col>
<el-col :span="12" class="input-value">
{{ securityData.mailbox }}
</el-col>
<el-col :span="6" class="action">
<span v-if="securityData.mailbox">
<el-button type="text" @click="openDialog('mailbox', 'unbind')">
解绑
</el-button>
<el-button type="text" @click="openDialog('mailbox', 'change')">
换绑
</el-button>
</span>
<span v-else>
<el-button
type="text"
style="color: orange"
@click="openDialog('mailbox', 'bind')">
绑定邮箱
</el-button>
</span>
</el-col>
</el-row>
<el-divider />
<el-row class="input-group" align="middle" :gutter="20">
<el-col :span="6" class="input-label">超级密码</el-col>
<el-col :span="12" class="input-value">
{{ securityData.superPassword ? '******' : '' }}
</el-col>
<el-col :span="6" class="action">
<span v-if="securityData.superPassword">
<el-button
type="text"
@click="openDialog('superPassword', 'password')">
修改超级密码
</el-button>
</span>
<span v-else>
<el-button
type="text"
style="color: orange"
@click="handleAddSuperPassword">
设置超级密码
</el-button>
</span>
</el-col>
</el-row>
</div>
<el-dialog
:visible.sync="dialogVisible"
:title="dialogTitle"
width="400px"
class="dialog"
:destroy-on-close="true"
:close-on-click-modal="false"
@close="resetForm">
<el-form
:model="dialogForm"
:rules="dialogRules"
ref="dialogForm"
:label-width="
dialogType === 'password' || dialogType === 'addPassword'
? '110px'
: '80px'
">
<el-form-item
v-if="currentMethod === 'phone'"
label="手机号"
prop="phone">
<el-input
v-model="dialogForm.phone"
placeholder="请输入手机号"
:disabled="dialogType !== 'bind'"
clearable
maxlength="11"
size="small" />
</el-form-item>
<el-form-item
v-if="currentMethod === 'mailbox'"
label="邮箱"
prop="mailbox">
<el-input
v-model="dialogForm.mailbox"
placeholder="请输入邮箱"
:disabled="dialogType !== 'bind'"
clearable
size="small" />
</el-form-item>
<el-form-item
v-if="currentMethod !== 'superPassword'"
label="验证码"
prop="code">
<div style="display: flex; align-items: center; gap: 10px">
<el-input
v-model="dialogForm.code"
placeholder="请输入验证码"
clearable
maxlength="6"
size="small" />
<el-button
size="small"
@click="sendVerificationCode"
:loading="countdownConfig[currentMethod]?.loading">
{{
countdownConfig[currentMethod]?.loading
? `${countdownConfig[currentMethod].countdown}s后重发`
: '发送验证码'
}}
</el-button>
</div>
</el-form-item>
<el-form-item
v-if="
(dialogType === 'password' && currentMethod === 'superPassword') ||
currentMethod === 'superPassword'
"
:label="dialogType === 'password' ? '原超级密码' : '超级密码'"
prop="superPassword">
<el-input
v-model="dialogForm.superPassword"
:placeholder="
dialogType === 'password' ? '请输入原超级密码' : '请输入超级密码'
"
clearable
show-password
size="small" />
</el-form-item>
<el-form-item
v-if="dialogType === 'password' || dialogType === 'addPassword'"
label="新超级密码"
prop="newSuperPassword1">
<el-input
v-model="dialogForm.newSuperPassword1"
placeholder="请输入新超级密码"
clearable
show-password
size="small" />
</el-form-item>
<el-form-item
v-if="dialogType === 'password' || dialogType === 'addPassword'"
label="确认超级密码"
prop="newSuperPassword2">
<el-input
v-model="dialogForm.newSuperPassword2"
placeholder="请再次输入新超级密码"
clearable
show-password
size="small" />
</el-form-item>
</el-form>
<el-divider v-if="dialogType !== 'bind'">其他验证方式</el-divider>
<div v-if="dialogType !== 'bind'" class="check">
<el-button
icon="el-icon-mobile-phone"
type="warning"
size="small"
v-if="currentMethod !== 'phone' && securityData.phone"
@click="currentMethod = 'phone'"
style="width: 100%">
手机验证
</el-button>
<el-button
icon="el-icon-message"
type="success"
size="small"
v-if="currentMethod !== 'mailbox' && securityData.mailbox"
@click="currentMethod = 'mailbox'"
style="width: 100%">
邮箱验证
</el-button>
<el-button
icon="el-icon-lock"
type="info"
size="small"
v-if="
currentMethod !== 'superPassword' &&
dialogType !== 'addPassword' &&
securityData.superPassword
"
@click="currentMethod = 'superPassword'"
style="width: 100%">
超级密码验证
</el-button>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small">取消</el-button>
<el-button type="primary" @click="handleSubmit" size="small">
{{ dialogType === 'change' ? '下一步' : '确定' }}
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import axios from '../../common/api/axios'
export default {
name: 'sysSecuritySettings',
data() {
return {
securityData: { phone: '', mailbox: '', superPassword: '' },
loading: true,
dialogVisible: false,
currentTarget: '',
currentMethod: '',
dialogType: '',
dialogForm: {
phone: '',
mailbox: '',
code: '',
superPassword: '',
newSuperPassword1: '',
newSuperPassword2: ''
},
countdownConfig: {
phone: { countdown: 0, loading: false, timer: null },
mailbox: { countdown: 0, loading: false, timer: null }
},
methodMap: {
phone: { name: '手机号', method: '手机' },
mailbox: { name: '邮箱', method: '邮箱' },
superPassword: { name: '超级密码', method: '超级密码' }
},
dialogRules: {}
}
},
computed: {
dialogTitle() {
if (
this.dialogType === 'change' &&
this.currentTarget === 'superPassword'
) {
return '身份验证'
}
const targetName = this.methodMap[this.currentTarget]?.name || '超级密码'
const actionMap = {
bind: this.securityData[this.currentTarget]
? `绑定新安全${targetName}`
: `绑定安全${targetName}`,
unbind: `解绑安全${targetName}`,
change: `验证原安全${targetName}`,
password: '修改超级密码',
addPassword: '新增超级密码'
}
return actionMap[this.dialogType] || '操作'
}
},
mounted() {
this.getList()
},
watch: {
currentMethod() {
this.$nextTick(() => this.$refs.dialogForm?.clearValidate())
}
},
methods: {
validateNewPassword(rule, value, callback) {
if (value !== this.dialogForm.newSuperPassword1) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
}
},
async getList() {
this.loading = true
try {
const res = await axios.post('sysSecuritySettings/list_page', {
pageSize: 100,
currentPage: 1
})
if (res.code === 200 && res.data.records?.length > 0) {
this.securityData = { ...res.data.records[0] }
}
} catch (error) {
console.error(error)
} finally {
this.loading = false
}
},
handleAddSuperPassword() {
const verificationMethod =
this.securityData.phone || this.securityData.mailbox
if (!verificationMethod) {
this.$message.warning('请先绑定手机或邮箱,才能进行此操作')
return
}
this.openDialog(verificationMethod, 'addPassword')
},
openDialog(method, type, targetOverride = null) {
this.currentMethod =
type === 'addPassword'
? this.securityData.phone
? 'phone'
: 'mailbox'
: method
this.currentTarget = targetOverride || method
this.dialogType = type
this.dialogForm = {
phone: type === 'bind' ? '' : this.securityData.phone || '',
mailbox: type === 'bind' ? '' : this.securityData.mailbox || '',
code: '',
superPassword: '',
newSuperPassword1: '',
newSuperPassword2: ''
}
this.dialogRules = this.getDialogRules(this.currentMethod, type)
this.dialogVisible = true
this.$nextTick(() => this.$refs.dialogForm?.clearValidate())
},
getDialogRules(method, type) {
if (type === 'addPassword') {
return {
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{
pattern: /^[0-9]{6}$/,
message: '请输入6位数字验证码',
trigger: 'blur'
}
],
newSuperPassword1: [
{ required: true, message: '请输入新超级密码', trigger: 'blur' }
],
newSuperPassword2: [
{
required: true,
message: '请再次输入新超级密码',
trigger: 'blur'
},
{ validator: this.validateNewPassword, trigger: 'blur' }
]
}
}
const rules = {
phone:
type === 'bind' && method === 'phone'
? [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的手机号',
trigger: 'blur'
}
]
: [],
mailbox:
type === 'bind' && method === 'mailbox'
? [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{
pattern:
/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.(com|cn|net|org|edu|gov|io)$/,
message: '请输入正确的邮箱',
trigger: 'blur'
}
]
: [],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{
pattern: /^[0-9]{6}$/,
message: '请输入6位数字验证码',
trigger: 'blur'
}
],
superPassword: [
{
required: true,
message: `请输入${type === 'password' ? '原超级密码' : '超级密码'}`,
trigger: 'blur'
}
],
newSuperPassword1: [
{ required: true, message: '请输入新超级密码', trigger: 'blur' }
],
newSuperPassword2: [
{
required: true,
message: '请再次输入新超级密码',
trigger: 'blur'
},
{ validator: this.validateNewPassword, trigger: 'blur' }
]
}
return rules
},
async sendVerificationCode() {
this.$refs.dialogForm.validateField(this.currentMethod, async (error) => {
if (error) return
const config = this.countdownConfig[this.currentMethod]
if (!config) return
if (config.timer) clearTimeout(config.timer)
config.loading = true
config.countdown = 60
const countdownFn = () => {
if (config.countdown > 0) {
config.countdown--
config.timer = setTimeout(countdownFn, 1000)
} else {
config.loading = false
config.timer = null
}
}
countdownFn()
try {
const url =
this.currentMethod === 'phone'
? '/sysSecuritySettings/sendPhoneCode'
: `/sysSecuritySettings/sendEmailCode?email=${this.dialogForm.mailbox}&operateType=${this.dialogTitle}`
const res = await axios({
method: this.currentMethod === 'phone' ? 'post' : 'get',
url,
data:
this.currentMethod === 'phone'
? {
phone: this.dialogForm.phone,
operateType: this.dialogTitle
}
: null
})
if (res.code !== 200) throw new Error('发送失败')
this.$message.success('验证码发送成功')
} catch (error) {
config.loading = false
config.countdown = 0
if (config.timer) clearTimeout(config.timer)
config.timer = null
this.$message.error('验证码发送失败')
}
})
},
async handleSubmit() {
this.$refs.dialogForm.validate(async (valid) => {
if (!valid) return
try {
const actionConfig = {
bind: {
url: '/sysSecuritySettings/changeBinding',
message: '绑定成功'
},
unbind: { url: '/sysSecuritySettings/unbind', message: '解绑成功' },
change: { url: '/sysSecuritySettings/check', message: '验证成功' },
password: {
url: '/sysSecuritySettings/updatePassword',
message: '超级密码修改成功'
},
addPassword: {
url: '/sysSecuritySettings/updatePassword',
message: '超级密码修改成功'
}
}
const { url, message } = actionConfig[this.dialogType]
const filteredForm = Object.fromEntries(
Object.entries(this.dialogForm).filter(([, v]) => v)
)
const res = await axios.post(url, {
...filteredForm,
checkType: this.currentMethod,
unbindType: this.currentTarget
})
if (res.code !== 200) throw new Error('操作失败')
this.$message.success(message)
if (this.dialogType !== 'change') this.getList()
this.dialogVisible = false
if (this.dialogType === 'change') {
this.openDialog(this.currentTarget, 'bind')
}
} catch (error) {
this.$message.error('操作失败')
}
})
},
resetForm() {
this.$refs.dialogForm?.resetFields()
this.dialogForm = {
phone: '',
mailbox: '',
code: '',
superPassword: '',
newSuperPassword1: '',
newSuperPassword2: ''
}
}
}
}
</script>
<style scoped>
.wraper {
background: #fff;
display: flex;
flex-direction: column;
height: 100%;
align-items: center;
padding: 10px;
}
.input-group {
margin: 10px 0;
display: flex;
align-items: center;
}
.input-label {
text-align: left;
color: #494747;
font-weight: 500;
}
.input-value {
text-align: right;
color: #333;
word-break: break-all;
}
.action {
text-align: right;
}
.card-content {
width: 600px;
flex: 1;
display: flex;
flex-direction: column;
margin: 100px 0 400px 0;
border: 1px solid rgb(235, 232, 232);
padding: 60px;
border-radius: 30px;
box-shadow: 0 8px 24px 0 rgba(0, 0, 0, 0.12),
0 1.5px 6px 0 rgba(0, 0, 0, 0.08);
}
.el-divider {
background-color: #dcdfe6c2;
}
.card-content .el-divider--horizontal {
margin: 5px;
}
.el-dialog {
padding: 20px 15px 20px 10px;
border-radius: 5px;
}
.dialog .el-divider--horizontal {
margin-top: 30px;
width: 80%;
margin-left: auto;
margin-right: auto;
}
.dialog .el-divider__text {
color: #8b8a8a;
}
.dialog-footer {
margin-top: 10px;
}
.check {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
width: 80%;
margin: 0 auto;
}
.check .el-button + .el-button {
margin-left: 0;
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment