Files
klp-oa/klp-ui/src/components/DictSelect/index.vue
砂糖 e34e5a7a1f feat(DictSelect): 支持多选模式并添加标签折叠功能
feat(meal): 修改用餐人数为成员选择并显示详细名单

feat: 新增用餐报表页面,包含数据统计和可视化图表
2026-01-21 16:24:57 +08:00

336 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div style="display: flex; align-items: center;" v-loading="loading">
<!-- 下拉选择器绑定计算属性做双向绑定保留原有样式+清空功能 -->
<el-select
v-model="innerValue"
:placeholder="placeholder"
clearable filterable
style="width: 200px;"
:multiple="multiple" collapse-tags>
<el-option
v-for="item in dictOptions"
:key="item.dictValue"
:label="item.dictLabel"
:value="item.dictValue"
/>
</el-select>
<!-- 编辑按钮点击打开弹窗 -->
<div
v-if="editable"
@click="openDictDialog"
style="cursor: pointer; height: 24px; width: 24px; margin-top: 4px; display: flex; align-items: center; justify-content: center; border: 1px solid #828991; margin-left: 8px; border-radius: 2px;"
>
<i class="el-icon-setting"></i>
</div>
<div
v-if="refresh"
@click="handleRefresh"
style="cursor: pointer; height: 24px; width: 24px; margin-top: 4px; display: flex; align-items: center; justify-content: center; border: 1px solid #828991; margin-left: 8px; border-radius: 2px;"
>
<i class="el-icon-refresh"></i>
</div>
<!-- 字典编辑弹窗 -->
<el-dialog
v-if="editable"
:visible.sync="open"
title="字典数据配置"
width="600px"
append-to-body
>
<el-form
ref="dictFormRef"
:model="form"
:rules="dictRules"
label-width="68px"
label-position="left"
style="margin-bottom: 20px;"
>
<el-row :gutter="15">
<el-col :span="kisv ? 24 : 12">
<el-form-item label="值/标签" prop="dictValue" v-if="kisv">
<el-input v-model="form.dictValue" placeholder="请输入字典值(标签自动同步)" />
</el-form-item>
<el-form-item label="字典标签" prop="dictLabel" v-else>
<el-input v-model="form.dictLabel" placeholder="请输入字典标签" />
</el-form-item>
</el-col>
<el-col :span="12" v-if="!kisv">
<el-form-item label="字典值" prop="dictValue">
<el-input v-model="form.dictValue" placeholder="请输入字典值" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div style="margin-bottom: 20px;">
<el-button type="primary" size="small" @click="submitAddDict" :loading="btnLoading">新增字典项</el-button>
<el-button size="small" @click="resetDictForm">清空表单</el-button>
<span style="margin-left: 10px;">提示双击单元格中的文字可以快速编辑</span>
</div>
<!-- 字典列表 核心修改双击单元格激活输入框失焦还原文本 -->
<el-table
:data="dictOptions"
border
stripe
size="mini"
style="width: 100%;"
empty-text="暂无字典数据"
>
<!-- 字典标签列kisv=true隐藏false正常展示 -->
<el-table-column label="字典标签" align="center" v-if="!kisv">
<template #default="scope">
<!-- 双击span触发编辑失焦/回车后变回span -->
<span
v-if="editRowId !== scope.row.dictCode"
@dblclick.stop="handleDbEdit(scope.row)"
style="cursor: pointer;"
>
{{ scope.row.dictLabel }}
</span>
<el-input
v-else
v-model="scope.row.dictLabel"
size="mini"
style="width: 100%;"
@change="handleSaveRow(scope.row)"
auto-focus
/>
</template>
</el-table-column>
<!-- 字典值列kisv=true时标题合并 -->
<el-table-column :label="kisv ? '字典值/标签' : '字典值'" align="center">
<template #default="scope">
<!-- 核心默认文本双击才显示输入框失焦立刻消失 -->
<span
v-if="editRowId !== scope.row.dictCode"
@dblclick.stop="handleDbEdit(scope.row)"
style="cursor: pointer;"
>
{{ scope.row.dictValue }}
</span>
<el-input
v-else
v-model="scope.row.dictValue"
size="mini"
style="width: 100%;"
@input="() => handleKisvTableSync(scope.row)"
@change="handleSaveRow(scope.row)"
auto-focus
/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template #default="scope">
<el-button
icon="el-icon-delete"
size="mini"
type="text"
text-danger
@click="handleDelete(scope.row)"
:loading="delBtnLoading === scope.row.dictCode"
>删除</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script>
import { addData, updateData, delData, listData } from '@/api/system/dict/data'
import { listType } from '@/api/system/dict/type'
export default {
name: 'DictSelectEdit',
props: {
dictType: { type: String, default: '' },
editable: { type: Boolean, default: true },
kisv: { type: Boolean, default: false },
value: { type: String, default: '' },
placeholder: { type: String, default: '请选择' },
refresh: { type: Boolean, default: true },
multiple: { type: Boolean, default: false },
},
data() {
return {
dictOptions: [],
dictId: '',
loading: false,
btnLoading: false,
delBtnLoading: '',
open: false,
editRowId: '', // 控制当前编辑行,为空则所有单元格都是文本状态
form: {
dictId: '',
dictLabel: '',
dictValue: '',
dictType: '',
sort: 0
},
dictRules: {
dictLabel: [{ required: true, message: '请输入字典标签', trigger: 'blur' }],
dictValue: [{ required: true, message: '请输入字典值', trigger: 'blur' }]
},
dictFormRef: null
}
},
computed: {
innerValue: {
get() {
if (this.multiple) {
if (!this.value) return []
return this.value?.split(',') || []
}
return this.value
},
set(val) {
if (this.multiple) {
this.$emit('input', val?.join(',') || '')
this.$emit('change', val)
} else {
this.$emit('input', val)
this.$emit('change', val)
}
}
}
},
watch: {
dictType: {
async handler(newVal) {
if (newVal) {
this.loading = true
try {
const dictId = await this.getDictId(newVal)
await this.getDictOptions(dictId)
} catch (err) {
console.error('加载字典失败:', err)
} finally {
this.loading = false
}
}
},
immediate: true
},
'form.dictValue': {
handler(val) {
if (this.kisv && val) this.form.dictLabel = val
},
immediate: true
}
},
methods: {
async getDictId(type) {
const res = await listType({ dictType: type })
if (res.rows?.length !== 1) {
this.$message.error('字典类型异常,未查询到对应配置')
return Promise.reject('字典类型异常')
}
this.dictId = res.rows[0].dictId
return this.dictId
},
// 新增:刷新字典数据
async handleRefresh() {
this.loading = true
try {
await this.getDictOptions(this.dictId)
} catch (err) {
console.error('刷新字典失败:', err)
} finally {
this.loading = false
}
},
async getDictOptions(dictId) {
const res = await listData({ dictType: this.dictType, pageSize: 1000 })
this.dictOptions = res.rows || []
return this.dictOptions
},
openDictDialog() {
this.open = true
this.resetDictForm()
this.form.dictId = this.dictId
this.form.dictType = this.dictType
this.editRowId = '' // 打开弹窗重置编辑状态
},
resetDictForm() {
this.form = { dictId: this.dictId, dictLabel: '', dictValue: '', dictType: this.dictType, sort: 0 }
this.$refs.dictFormRef && this.$refs.dictFormRef.clearValidate()
},
handleKisvTableSync(row) {
if (this.kisv && row.dictValue) row.dictLabel = row.dictValue
},
async submitAddDict() {
const valid = await this.$refs.dictFormRef.validate().catch(() => false)
if (!valid) return
this.loading = true
this.btnLoading = true
try {
await addData(this.form)
this.$message.success('字典项新增成功!')
await this.getDictOptions(this.dictId)
this.resetDictForm()
} catch (err) {
this.$message.error('新增失败,请稍后重试')
console.error(err)
} finally {
this.loading = false
this.btnLoading = false
}
},
// ✅ 新增:双击单元格 激活编辑状态 核心方法
handleDbEdit(row) {
// 同一时间只允许编辑一行,双击其他行关闭当前行编辑
this.editRowId = row.dictCode
},
// ✅ 核心完善:失去焦点/回车 保存数据 + 强制关闭编辑态 → 还原成普通单元格
async handleSaveRow(row) {
if (!row.dictLabel || !row.dictValue) {
this.$message.warning('字典标签和字典值不能为空!')
await this.getDictOptions(this.dictId)
this.editRowId = '' // 校验失败,也必须还原单元格
return
}
row.sort = 0
this.loading = true
try {
await updateData(row)
this.$message.success('字典项修改成功!')
} catch (err) {
this.$message.error('修改失败,请稍后重试')
await this.getDictOptions(this.dictId)
console.error(err)
} finally {
this.loading = false
this.editRowId = '' // ✅必加:保存完成,立刻关闭编辑态,变回文本
}
},
async handleDelete(row) {
const confirm = await this.$confirm('确定要删除该字典项吗?删除后不可恢复!', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).catch(() => false)
if (!confirm) return
this.loading = true
this.delBtnLoading = row.dictCode
try {
await delData(row.dictCode)
this.$message.success('删除成功!')
await this.getDictOptions(this.dictId)
} catch (err) {
this.$message.error('删除失败,请稍后重试')
console.error(err)
} finally {
this.loading = false
this.delBtnLoading = ''
}
}
}
}
</script>