feat: 新增数据管理模块(区域管理)及修复前端已知问题
后端(bms-back):
- 新增区域管理完整后端:Domain/Mapper/Service/Controller + MyBatis XML
- 新增 RestTemplateConfig 配置类,用于第三方API调用
- 新增 countries.json 本地国家数据源(105个国家)
- RuoYiApplication 加 @EnableAsync 支持异步任务
- application-druid.yml 修复 Druid 连接池字符集配置(SET NAMES utf8mb4)
- logback.xml 日志路径改为 ${user.dir}/logs 避免沙箱写入受限
- pom.xml 增加 JVM 编码参数(-Dfile.encoding=UTF-8)
- 新增 sql/bms_region.sql 建表脚本+菜单权限
前端(bms-front):
- 新增区域管理页面(列表/编辑/状态切换/同步国家数据/导出)
- 新增同步日志功能(内嵌为区域管理弹窗,移除独立菜单)
- 新增 API 层: region.js / region-log.js
- 修复 BasicForm/index.vue 缺失 import FormControl 导致搜索栏控件不渲染
- 修复 user/role/region 三个页面 el-switch 初始化时弹 undefined 提示
This commit is contained in:
@@ -49,5 +49,10 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"quill": "2.0.2"
|
||||
},
|
||||
"allowScripts": {
|
||||
"esbuild@0.25.12": true,
|
||||
"@parcel/watcher@2.6.0": true,
|
||||
"vue-demi@0.14.10": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询同步日志列表
|
||||
export function listSyncLog(query) {
|
||||
return request({
|
||||
url: '/data/region/log/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询同步日志详细
|
||||
export function getSyncLog(logId) {
|
||||
return request({
|
||||
url: '/data/region/log/' + logId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除同步日志
|
||||
export function delSyncLog(logIds) {
|
||||
return request({
|
||||
url: '/data/region/log/' + logIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询区域列表
|
||||
export function listRegion(query) {
|
||||
return request({
|
||||
url: '/data/region/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询区域详细
|
||||
export function getRegion(regionId) {
|
||||
return request({
|
||||
url: '/data/region/' + regionId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改区域
|
||||
export function updateRegion(data) {
|
||||
return request({
|
||||
url: '/data/region',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除区域
|
||||
export function delRegion(regionIds) {
|
||||
return request({
|
||||
url: '/data/region/' + regionIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改区域状态
|
||||
export function changeRegionStatus(regionId, status) {
|
||||
const data = {
|
||||
regionId,
|
||||
status
|
||||
}
|
||||
return request({
|
||||
url: '/data/region/changeStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 同步国家数据
|
||||
export function syncRegion() {
|
||||
return request({
|
||||
url: '/data/region/sync',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
@@ -91,6 +91,8 @@
|
||||
* colProps: {} // 透传给 el-col 的属性
|
||||
* }
|
||||
*/
|
||||
import { ref, computed, reactive, isRef, watch, nextTick, getCurrentInstance } from "vue"
|
||||
import FormControl from "./FormControl.vue"
|
||||
|
||||
defineOptions({ name: 'BasicForm' })
|
||||
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
row-key="regionId"
|
||||
>
|
||||
<template #toolbar="{ selection, ids }">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Refresh"
|
||||
:loading="syncLoading"
|
||||
@click="handleSync"
|
||||
v-hasPermi="['data:region:sync']"
|
||||
>同步国家数据</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="info"
|
||||
plain
|
||||
icon="List"
|
||||
@click="openSyncLogDialog"
|
||||
v-hasPermi="['data:region:log:list']"
|
||||
>同步日志</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="selection.length !== 1"
|
||||
@click="handleUpdate(selection[0])"
|
||||
v-hasPermi="['data:region:edit']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="!selection.length"
|
||||
@click="handleDelete(ids)"
|
||||
v-hasPermi="['data:region:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['data:region:list']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<template #col-status="{ row }">
|
||||
<el-switch
|
||||
v-model="row.status"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
@change="handleStatusChange(row)"
|
||||
v-hasPermi="['data:region:edit']"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #col-flagEmoji="{ row }">
|
||||
<span style="font-size: 18px">{{ row.flagEmoji }}</span>
|
||||
</template>
|
||||
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(row)" v-hasPermi="['data:region:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(row.regionId)" v-hasPermi="['data:region:remove']">删除</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
<!-- 修改区域对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="600px" append-to-body>
|
||||
<el-form ref="regionRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="中文名" prop="countryNameCn">
|
||||
<el-input v-model="form.countryNameCn" placeholder="请输入中文名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="英文名" prop="countryNameEn">
|
||||
<el-input v-model="form.countryNameEn" placeholder="请输入英文名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="ISO两位码" prop="countryCode2">
|
||||
<el-input v-model="form.countryCode2" placeholder="如: DE" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="ISO三位码" prop="countryCode3">
|
||||
<el-input v-model="form.countryCode3" placeholder="如: DEU" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="洲/大区" prop="continent">
|
||||
<el-input v-model="form.continent" placeholder="如: Europe" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="子区域" prop="subRegion">
|
||||
<el-input v-model="form.subRegion" placeholder="如: Western Europe" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="货币代码" prop="currencyCode">
|
||||
<el-input v-model="form.currencyCode" placeholder="如: EUR" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="电话前缀" prop="phonePrefix">
|
||||
<el-input v-model="form.phonePrefix" placeholder="如: +49" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否独立" prop="isIndependent">
|
||||
<el-radio-group v-model="form.isIndependent">
|
||||
<el-radio value="1">是</el-radio>
|
||||
<el-radio value="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否欧盟" prop="isEu">
|
||||
<el-radio-group v-model="form.isEu">
|
||||
<el-radio value="1">是</el-radio>
|
||||
<el-radio value="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 同步日志对话框 -->
|
||||
<el-dialog title="同步日志" v-model="logDialogVisible" width="1200px" append-to-body top="8vh">
|
||||
<basic-table
|
||||
ref="logTableRef"
|
||||
:columns="logColumns"
|
||||
:search-columns="logSearchColumns"
|
||||
:query="logQueryList"
|
||||
row-key="logId"
|
||||
:show-search="true"
|
||||
>
|
||||
<template #toolbar="{ selection, ids }">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="!selection.length"
|
||||
@click="handleLogDelete(ids)"
|
||||
v-hasPermi="['data:region:log:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<template #col-status="{ row }">
|
||||
<el-tag :type="logStatusType(row.status)">
|
||||
{{ logStatusText(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</basic-table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Region">
|
||||
import { listRegion, getRegion, updateRegion, delRegion, changeRegionStatus, syncRegion } from "@/api/data/region"
|
||||
import { getSyncLog, listSyncLog, delSyncLog } from "@/api/data/region-log"
|
||||
import { nextTick, onBeforeUnmount } from "vue"
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const tableRef = ref()
|
||||
const open = ref(false)
|
||||
const title = ref("")
|
||||
const syncLoading = ref(false)
|
||||
const syncLogId = ref(null)
|
||||
const syncTimer = ref(null)
|
||||
|
||||
// ====== 同步日志对话框 ======
|
||||
const logDialogVisible = ref(false)
|
||||
const logTableRef = ref()
|
||||
const logSearchColumns = [
|
||||
{ label: "状态", prop: "status", component: "select", options: [
|
||||
{ label: "待处理", value: "PENDING" },
|
||||
{ label: "处理中", value: "PROCESSING" },
|
||||
{ label: "成功", value: "SUCCESS" },
|
||||
{ label: "失败", value: "FAILED" }
|
||||
], placeholder: "状态", width: "160px" },
|
||||
{ label: "创建时间", prop: "dateRange", component: "daterange", width: "320px" }
|
||||
]
|
||||
const logColumns = [
|
||||
{ label: "日志ID", prop: "logId", align: "center", width: 80 },
|
||||
{ label: "同步类型", prop: "syncType", align: "center", width: 100 },
|
||||
{ label: "状态", prop: "status", align: "center", width: 100 },
|
||||
{ label: "总数", prop: "totalCount", align: "center", width: 80 },
|
||||
{ label: "新增数", prop: "insertCount", align: "center", width: 80 },
|
||||
{ label: "更新数", prop: "updateCount", align: "center", width: 80 },
|
||||
{ label: "失败数", prop: "failCount", align: "center", width: 80 },
|
||||
{ label: "耗时(ms)", prop: "durationMs", align: "center", width: 100 },
|
||||
{ label: "错误信息", prop: "errorMsg", align: "center", showOverflowTooltip: true },
|
||||
{ label: "创建时间", prop: "createTime", align: "center", width: 180, parseTime: true }
|
||||
]
|
||||
function logQueryList(params) {
|
||||
const p = proxy.addDateRange({ ...params }, params.dateRange)
|
||||
delete p.dateRange
|
||||
return listSyncLog(p)
|
||||
}
|
||||
function logStatusText(status) {
|
||||
const map = { PENDING: "待处理", PROCESSING: "处理中", SUCCESS: "成功", FAILED: "失败" }
|
||||
return map[status] || status
|
||||
}
|
||||
function logStatusType(status) {
|
||||
const map = { PENDING: "info", PROCESSING: "warning", SUCCESS: "success", FAILED: "danger" }
|
||||
return map[status] || "info"
|
||||
}
|
||||
function handleLogDelete(logIds) {
|
||||
proxy.$modal.confirm('是否确认删除日志编号为"' + logIds + '"的数据项?').then(function () {
|
||||
return delSyncLog(logIds)
|
||||
}).then(() => {
|
||||
logTableRef.value.refresh()
|
||||
proxy.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
function openSyncLogDialog() {
|
||||
logDialogVisible.value = true
|
||||
nextTick(() => { logTableRef.value && logTableRef.value.reload() })
|
||||
}
|
||||
|
||||
// 搜索项配置
|
||||
const searchColumns = [
|
||||
{ label: "中文名", prop: "countryNameCn", component: "input", placeholder: "请输入中文名", width: "200px" },
|
||||
{ label: "英文名", prop: "countryNameEn", component: "input", placeholder: "请输入英文名", width: "200px" },
|
||||
{ label: "国家代码", prop: "countryCode2", component: "input", placeholder: "如: DE", width: "160px" },
|
||||
{ label: "洲/大区", prop: "continent", component: "input", placeholder: "如: Europe", width: "200px" },
|
||||
{ label: "状态", prop: "status", component: "select", options: [{ label: "正常", value: "0" }, { label: "停用", value: "1" }], placeholder: "状态", width: "140px" }
|
||||
]
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{ label: "区域ID", prop: "regionId", align: "center", width: 80 },
|
||||
{ label: "国旗", prop: "flagEmoji", align: "center", width: 60 },
|
||||
{ label: "中文名", prop: "countryNameCn", align: "center" },
|
||||
{ label: "英文名", prop: "countryNameEn", align: "center" },
|
||||
{ label: "国家代码", prop: "countryCode2", align: "center", width: 80 },
|
||||
{ label: "ISO三位码", prop: "countryCode3", align: "center", width: 90 },
|
||||
{ label: "洲/大区", prop: "continent", align: "center", width: 120 },
|
||||
{ label: "子区域", prop: "subRegion", align: "center", width: 140 },
|
||||
{ label: "货币", prop: "currencyCode", align: "center", width: 80 },
|
||||
{ label: "电话前缀", prop: "phonePrefix", align: "center", width: 90 },
|
||||
{ label: "状态", prop: "status", align: "center", width: 80 },
|
||||
{ label: "操作", prop: "action", align: "center", width: 150, showInToolbar: false }
|
||||
]
|
||||
|
||||
// 查询函数
|
||||
function queryList(params) {
|
||||
return listRegion(params)
|
||||
}
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
rules: {
|
||||
countryNameEn: [{ required: true, message: "英文名不能为空", trigger: "blur" }],
|
||||
countryCode2: [{ required: true, message: "ISO两位码不能为空", trigger: "blur" }]
|
||||
}
|
||||
})
|
||||
|
||||
const { form, rules } = toRefs(data)
|
||||
|
||||
/** 取消按钮 */
|
||||
function cancel() {
|
||||
open.value = false
|
||||
reset()
|
||||
}
|
||||
|
||||
/** 表单重置 */
|
||||
function reset() {
|
||||
form.value = {
|
||||
regionId: undefined,
|
||||
countryNameCn: undefined,
|
||||
countryNameEn: undefined,
|
||||
countryCode2: undefined,
|
||||
countryCode3: undefined,
|
||||
continent: undefined,
|
||||
subRegion: undefined,
|
||||
currencyCode: undefined,
|
||||
phonePrefix: undefined,
|
||||
isIndependent: "1",
|
||||
isEu: "0",
|
||||
remark: undefined
|
||||
}
|
||||
proxy.resetForm("regionRef")
|
||||
}
|
||||
|
||||
/** 修改按钮操作 */
|
||||
function handleUpdate(row) {
|
||||
reset()
|
||||
const regionId = row.regionId
|
||||
getRegion(regionId).then(response => {
|
||||
form.value = response.data
|
||||
open.value = true
|
||||
title.value = "修改区域"
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs["regionRef"].validate(valid => {
|
||||
if (valid) {
|
||||
updateRegion(form.value).then(() => {
|
||||
proxy.$modal.msgSuccess("修改成功")
|
||||
open.value = false
|
||||
tableRef.value.refresh()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(regionIds) {
|
||||
proxy.$modal.confirm('是否确认删除区域编号为"' + regionIds + '"的数据项?').then(function () {
|
||||
return delRegion(regionIds)
|
||||
}).then(() => {
|
||||
tableRef.value.refresh()
|
||||
proxy.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/** 修改状态 */
|
||||
function handleStatusChange(row) {
|
||||
// 优先显示:中文名 > 英文名 > 国家代码
|
||||
const displayName = row.countryNameCn || row.countryNameEn || row.countryCode2 || '';
|
||||
if (row.regionId === undefined || row.regionId === null || row.regionId === '' || !displayName) {
|
||||
nextTick(() => {
|
||||
row.status = row.status === "0" ? "1" : "0"
|
||||
})
|
||||
return
|
||||
}
|
||||
if (row.__originalStatus !== undefined && row.__originalStatus === row.status) {
|
||||
return
|
||||
}
|
||||
row.__originalStatus = row.status === "0" ? "1" : "0"
|
||||
|
||||
const text = row.status === "0" ? "启用" : "停用"
|
||||
proxy.$modal.confirm(`确认要${text}「${displayName}」国家/区域吗?`).then(function () {
|
||||
return changeRegionStatus(row.regionId, row.status)
|
||||
}).then(() => {
|
||||
row.__originalStatus = row.status
|
||||
proxy.$modal.msgSuccess(text + "成功")
|
||||
}).catch(function () {
|
||||
const restored = row.status === "0" ? "1" : "0"
|
||||
row.__originalStatus = restored
|
||||
nextTick(() => { row.status = restored })
|
||||
})
|
||||
}
|
||||
|
||||
/** 同步国家数据 */
|
||||
function handleSync() {
|
||||
proxy.$modal.confirm('确认从第三方API同步国家数据?').then(function () {
|
||||
syncLoading.value = true
|
||||
return syncRegion()
|
||||
}).then(res => {
|
||||
syncLogId.value = res.logId
|
||||
proxy.$modal.msgSuccess("同步任务已创建,正在后台执行...")
|
||||
// 轮询同步状态
|
||||
syncTimer.value = setInterval(() => {
|
||||
getSyncLog(syncLogId.value).then(response => {
|
||||
const log = response.data
|
||||
if (log.status === "SUCCESS") {
|
||||
clearInterval(syncTimer.value)
|
||||
syncTimer.value = null
|
||||
syncLoading.value = false
|
||||
tableRef.value.refresh()
|
||||
proxy.$modal.msgSuccess(`同步完成!总计${log.totalCount}条,新增${log.insertCount}条,更新${log.updateCount}条,失败${log.failCount}条`)
|
||||
} else if (log.status === "FAILED") {
|
||||
clearInterval(syncTimer.value)
|
||||
syncTimer.value = null
|
||||
syncLoading.value = false
|
||||
proxy.$modal.msgError("同步失败:" + log.errorMsg)
|
||||
}
|
||||
})
|
||||
}, 3000)
|
||||
}).catch(() => {
|
||||
syncLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download("data/region/export", {
|
||||
...tableRef.value.getQueryParams()
|
||||
}, `region_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
/** 组件卸载前清理定时器 */
|
||||
onBeforeUnmount(() => {
|
||||
if (syncTimer.value) {
|
||||
clearInterval(syncTimer.value)
|
||||
syncTimer.value = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
row-key="logId"
|
||||
>
|
||||
<template #toolbar="{ selection, ids }">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="!selection.length"
|
||||
@click="handleDelete(ids)"
|
||||
v-hasPermi="['data:region:log:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<template #col-status="{ row }">
|
||||
<el-tag :type="statusType(row.status)">
|
||||
{{ statusText(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</basic-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="RegionSyncLog">
|
||||
import { listSyncLog, delSyncLog } from "@/api/data/region-log"
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const tableRef = ref()
|
||||
|
||||
// 搜索项配置
|
||||
const searchColumns = [
|
||||
{ label: "状态", prop: "status", component: "select", options: [
|
||||
{ label: "待处理", value: "PENDING" },
|
||||
{ label: "处理中", value: "PROCESSING" },
|
||||
{ label: "成功", value: "SUCCESS" },
|
||||
{ label: "失败", value: "FAILED" }
|
||||
], placeholder: "状态", width: "160px" },
|
||||
{ label: "创建时间", prop: "dateRange", component: "daterange", width: "320px" }
|
||||
]
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{ label: "日志ID", prop: "logId", align: "center", width: 80 },
|
||||
{ label: "同步类型", prop: "syncType", align: "center", width: 100 },
|
||||
{ label: "状态", prop: "status", align: "center", width: 100 },
|
||||
{ label: "总数", prop: "totalCount", align: "center", width: 80 },
|
||||
{ label: "新增数", prop: "insertCount", align: "center", width: 80 },
|
||||
{ label: "更新数", prop: "updateCount", align: "center", width: 80 },
|
||||
{ label: "失败数", prop: "failCount", align: "center", width: 80 },
|
||||
{ label: "耗时(ms)", prop: "durationMs", align: "center", width: 100 },
|
||||
{ label: "错误信息", prop: "errorMsg", align: "center", showOverflowTooltip: true },
|
||||
{ label: "创建时间", prop: "createTime", align: "center", width: 180, parseTime: true }
|
||||
]
|
||||
|
||||
// 查询函数
|
||||
function queryList(params) {
|
||||
const p = proxy.addDateRange({ ...params }, params.dateRange)
|
||||
delete p.dateRange
|
||||
return listSyncLog(p)
|
||||
}
|
||||
|
||||
/** 状态文本 */
|
||||
function statusText(status) {
|
||||
const map = { PENDING: "待处理", PROCESSING: "处理中", SUCCESS: "成功", FAILED: "失败" }
|
||||
return map[status] || status
|
||||
}
|
||||
|
||||
/** 状态标签类型 */
|
||||
function statusType(status) {
|
||||
const map = { PENDING: "info", PROCESSING: "warning", SUCCESS: "success", FAILED: "danger" }
|
||||
return map[status] || "info"
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(logIds) {
|
||||
proxy.$modal.confirm('是否确认删除日志编号为"' + logIds + '"的数据项?').then(function () {
|
||||
return delSyncLog(logIds)
|
||||
}).then(() => {
|
||||
tableRef.value.refresh()
|
||||
proxy.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -179,6 +179,7 @@
|
||||
<script setup name="Role">
|
||||
import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect } from "@/api/system/role"
|
||||
import { roleMenuTreeselect, treeselect as menuTreeselect } from "@/api/system/menu"
|
||||
import { nextTick } from "vue"
|
||||
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
@@ -263,13 +264,28 @@ function handleExport() {
|
||||
|
||||
/** 角色状态修改 */
|
||||
function handleStatusChange(row) {
|
||||
let text = row.status === "0" ? "启用" : "停用"
|
||||
proxy.$modal.confirm('确认要"' + text + '""' + row.roleName + '"角色吗?').then(function () {
|
||||
if (row.roleId === undefined || row.roleId === null || row.roleId === '' ||
|
||||
row.roleName === undefined || row.roleName === null || row.roleName === '') {
|
||||
nextTick(() => {
|
||||
row.status = row.status === "0" ? "1" : "0"
|
||||
})
|
||||
return
|
||||
}
|
||||
if (row.__originalStatus !== undefined && row.__originalStatus === row.status) {
|
||||
return
|
||||
}
|
||||
row.__originalStatus = row.status === "0" ? "1" : "0"
|
||||
|
||||
const text = row.status === "0" ? "启用" : "停用"
|
||||
proxy.$modal.confirm(`确认要${text}「${row.roleName}」角色吗?`).then(function () {
|
||||
return changeRoleStatus(row.roleId, row.status)
|
||||
}).then(() => {
|
||||
row.__originalStatus = row.status
|
||||
proxy.$modal.msgSuccess(text + "成功")
|
||||
}).catch(function () {
|
||||
row.status = row.status === "0" ? "1" : "0"
|
||||
const restored = row.status === "0" ? "1" : "0"
|
||||
row.__originalStatus = restored
|
||||
nextTick(() => { row.status = restored })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -161,6 +161,7 @@ import ExcelImportDialog from "@/components/ExcelImportDialog"
|
||||
import UserViewDrawer from "./view"
|
||||
import { usePasswordRule } from "@/utils/passwordRule"
|
||||
import { changeUserStatus, listUser, resetUserPwd, delUser, getUser, updateUser, addUser, deptTreeSelect } from "@/api/system/user"
|
||||
import { nextTick } from "vue"
|
||||
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
@@ -265,13 +266,33 @@ function handleExport() {
|
||||
|
||||
/** 用户状态修改 */
|
||||
function handleStatusChange(row) {
|
||||
let text = row.status === "0" ? "启用" : "停用"
|
||||
proxy.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function () {
|
||||
// 守卫:页面初始化/数据加载过程中 el-switch 可能发生赋值抖动,
|
||||
// userId 或 userName 缺失时不弹确认,只把状态反向重置防止显示偏差
|
||||
if (row.userId === undefined || row.userId === null || row.userId === '' ||
|
||||
row.userName === undefined || row.userName === null || row.userName === '') {
|
||||
nextTick(() => {
|
||||
row.status = row.status === "0" ? "1" : "0"
|
||||
})
|
||||
return
|
||||
}
|
||||
// 与原始记录中的 status 对比:status 只能是字符串 "0"/"1",若不一致(如 loading 中被改)则回滚
|
||||
if (row.__originalStatus !== undefined && row.__originalStatus === row.status) {
|
||||
return
|
||||
}
|
||||
// 为当前行记录一次原始值,后面 catch 时与回滚同步,避免重复触发 change
|
||||
row.__originalStatus = row.status === "0" ? "1" : "0"
|
||||
|
||||
const text = row.status === "0" ? "启用" : "停用"
|
||||
proxy.$modal.confirm(`确认要${text}「${row.userName}」用户吗?`).then(function () {
|
||||
return changeUserStatus(row.userId, row.status)
|
||||
}).then(() => {
|
||||
row.__originalStatus = row.status
|
||||
proxy.$modal.msgSuccess(text + "成功")
|
||||
}).catch(function () {
|
||||
row.status = row.status === "0" ? "1" : "0"
|
||||
const restored = row.status === "0" ? "1" : "0"
|
||||
row.__originalStatus = restored
|
||||
// 回滚赋值时短暂忽略 change,防止递归弹框
|
||||
nextTick(() => { row.status = restored })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user