ljw 2026-03-04 14:36:17 +08:00
commit 4bddd5b24f
11 changed files with 379 additions and 540 deletions

View File

@ -225,6 +225,7 @@ const displayData = computed(() => {
<style lang="stylus" scoped> <style lang="stylus" scoped>
.drop-table { .drop-table {
width 100%; width 100%;
display inline-block
} }
.clear-btn { .clear-btn {

View File

@ -55,7 +55,7 @@ interface TableActionType<T> {
tooltip: string | ((data: ColumnScopeType<T>) => string) tooltip: string | ((data: ColumnScopeType<T>) => string)
loading?: boolean loading?: boolean
show?: (data: ColumnScopeType<T>) => boolean show?: (data: ColumnScopeType<T>) => boolean
action: (data: ColumnScopeType<T>) => Promise<boolean> | void action?: (data: ColumnScopeType<T>) => Promise<boolean> | void
confirm?: { confirm?: {
title: string | ((data: ColumnScopeType<T>) => string) title: string | ((data: ColumnScopeType<T>) => string)
width?: string width?: string
@ -81,6 +81,7 @@ interface ToolType {
type?: 'text' | 'default' | 'primary' | 'success' | 'warning' | 'info' | 'danger' type?: 'text' | 'default' | 'primary' | 'success' | 'warning' | 'info' | 'danger'
label?: string label?: string
action: () => void action: () => void
disabled: boolean
} }
interface ToolBarType { interface ToolBarType {
@ -94,6 +95,7 @@ interface TablePropsType<T extends DefaultRow, F extends object> extends Omit<Ta
* *
*/ */
actionColumn: ActionColumnType<T> actionColumn: ActionColumnType<T>
rowClickHandle?: (row: T) => void
} }
interface FormPropsType<P, T extends DefaultRow> { interface FormPropsType<P, T extends DefaultRow> {
@ -103,6 +105,7 @@ interface FormPropsType<P, T extends DefaultRow> {
*/ */
paging: (param: P) => Promise<R<G.PageResult<T>>> paging: (param: P) => Promise<R<G.PageResult<T>>>
list?: (param: P) => Promise<R<T[]>> list?: (param: P) => Promise<R<T[]>>
resetForm?: (param?: P) => P | null | undefined
/** /**
* *
* @param param * @param param
@ -289,9 +292,12 @@ function buildToolBar(toolBar: DeepPartial<ToolBarType> = {}) {
if (leftTool.type == null) { if (leftTool.type == null) {
leftTool.type = 'primary' leftTool.type = 'primary'
} }
if (leftTool.disabled == null) leftTool.disabled = false
} }
if (toolBar.rightTools == null) toolBar.rightTools = [] if (toolBar.rightTools == null) toolBar.rightTools = []
for (let rightTool of toolBar.rightTools) {
if (rightTool.disabled == null) rightTool.disabled = false
}
return toolBar return toolBar
} }
@ -342,7 +348,7 @@ const component = defineComponent(
return return
} }
} }
expose({doSearch, tableData}) expose({doSearch, tableData, formData})
const showHighFormHandle = () => { const showHighFormHandle = () => {
showHighForm.value = !showHighForm.value showHighForm.value = !showHighForm.value
formData.$reset() formData.$reset()
@ -361,7 +367,7 @@ const component = defineComponent(
if (action.loading != null) { if (action.loading != null) {
action.loading = true action.loading = true
} }
const result = action.action(data) const result = action.action?.(data)
if (result instanceof Promise) { if (result instanceof Promise) {
result result
.then(reload => { .then(reload => {
@ -378,7 +384,9 @@ const component = defineComponent(
} }
function doReset() { function doReset() {
formData.$reset() const resetFormData = props.searchForm.resetForm?.()
if (resetFormData == null) formData.$reset()
else formData.$reset(resetFormData as any)
doSearch() doSearch()
} }
@ -567,6 +575,7 @@ const component = defineComponent(
{ {
props.toolBar.leftTools.map((tool, i) => ( props.toolBar.leftTools.map((tool, i) => (
<ElButton key={'tool-bar-left-' + i} <ElButton key={'tool-bar-left-' + i}
disabled={tool.disabled}
icon={Strings.isBlank(tool.icon) ? undefined : elIcons[tool.icon!]} icon={Strings.isBlank(tool.icon) ? undefined : elIcons[tool.icon!]}
type={tool.type} type={tool.type}
onClick={tool.action}> onClick={tool.action}>
@ -604,6 +613,7 @@ const component = defineComponent(
props.toolBar.rightTools.map((tool, i) => ( props.toolBar.rightTools.map((tool, i) => (
<ElButton key={'tool-bar-right-' + i} <ElButton key={'tool-bar-right-' + i}
icon={elIcons[tool.icon]} icon={elIcons[tool.icon]}
disabled={tool.disabled}
onClick={tool.action}/> onClick={tool.action}/>
)) ))
} }
@ -636,6 +646,9 @@ const component = defineComponent(
onSort-change={sortChangeHandler} onSort-change={sortChangeHandler}
summary-method={props.table.summaryMethod} summary-method={props.table.summaryMethod}
show-summary={props.table.summaryMethod != null} show-summary={props.table.summaryMethod != null}
highlightCurrentRow={props.table.highlightCurrentRow}
currentRowKey={props.table.currentRowKey}
onRow-click={props.table.rowClickHandle}
> >
{ {
slots.columns?.() slots.columns?.()
@ -669,9 +682,10 @@ const component = defineComponent(
}, },
) )
export interface ATablePageInstance<T extends DefaultRow = DefaultRow> extends InstanceType<typeof component> { export interface ATablePageInstance<P extends G.PageParam = any, T extends DefaultRow = DefaultRow> extends InstanceType<typeof component> {
doSearch: () => void doSearch: () => void
tableData: T[] tableData: T[]
formData: ResetAble<P>
} }
export default component export default component

View File

@ -15,7 +15,7 @@
</ElFormItem> </ElFormItem>
<ElFormItem v-if="formData.expenseItemCategory === 'ChanPin'" label="产品" prop="goodsId"> <ElFormItem v-if="formData.expenseItemCategory === 'ChanPin'" label="产品" prop="goodsId">
<GoodsDropTable v-model="formData.goodsId"/> <GoodsDropTable v-model="formData.goodsId" :placeholder="formData.goodsName"/>
</ElFormItem> </ElFormItem>
<ElFormItem v-else label="项目名称" prop="expenseItemName"> <ElFormItem v-else label="项目名称" prop="expenseItemName">
<ElInput v-model="formData.expenseItemName" placeholder="项目名称"/> <ElInput v-model="formData.expenseItemName" placeholder="项目名称"/>

View File

@ -5,6 +5,7 @@ import { bizType } from '@/pages/gds/goods-category/constants.ts'
const props = defineProps<{ const props = defineProps<{
defaultBizType?: typeof bizType[number]['val'] defaultBizType?: typeof bizType[number]['val']
placeholder?: string
}>() }>()
const model = defineModel<string | undefined | null>() const model = defineModel<string | undefined | null>()
@ -29,5 +30,6 @@ const dropTableLoader = (param: GoodsTypes.SearchGoodsParam) => {
<ADropTable v-model="model" <ADropTable v-model="model"
:columns="dropTableColumns" :columns="dropTableColumns"
:loader="dropTableLoader" :loader="dropTableLoader"
:placeholder="placeholder"
display-field="goodsName"/> display-field="goodsName"/>
</template> </template>

View File

@ -1,10 +1,7 @@
<template> <template>
<APage> <APage class="dict-page">
<div class="dict-page"> <DictCategory :current-dict @search-dict="searchDict"/>
<DictCategory @search-dict="searchDict"/> <DictItem ref="dictItem"/>
<ElDivider direction="vertical"/>
<DictItem ref="dictItem"/>
</div>
</APage> </APage>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -13,8 +10,10 @@ import DictCategory from '@/pages/sys/dict/DictCategory.vue'
import DictItem from '@/pages/sys/dict/DictItem.vue' import DictItem from '@/pages/sys/dict/DictItem.vue'
const dictItemIns = useTemplateRef<InstanceType<typeof DictItem>>('dictItem') const dictItemIns = useTemplateRef<InstanceType<typeof DictItem>>('dictItem')
const currentDict = ref<string | undefined>(undefined)
function searchDict(data: DictTypes.SearchDictResult) { function searchDict(data: DictTypes.SearchDictResult) {
currentDict.value = data.id
dictItemIns.value?.reload(data) dictItemIns.value?.reload(data)
} }
@ -24,15 +23,10 @@ function searchDict(data: DictTypes.SearchDictResult) {
display flex display flex
justify-content space-between justify-content space-between
flex-direction: row; flex-direction: row;
gap: 50px;
& > div:not(:nth-child(2)) { & > div {
width 50% width 50%
box-sizing border-box box-sizing border-box
} }
& > div:nth-child(2) {
height: 100%;
}
} }
</style> </style>

View File

@ -1,193 +1,109 @@
<template> <template>
<div> <ATablePage
<div class="tool-bar"> ref="tablePage"
<ElForm inline @submit.prevent="paging"> v-bind="tablePageProps">
<ElFormItem> <template #simpleFormItem="formData">
<ElInput <ElFormItem>
v-model="searchForm.dictName" <ElInput
clearable v-model="formData.dictName"
placeholder="请输入要搜索的文字" clearable
@clear="reset"/> placeholder="请输入要搜索的文字"
</ElFormItem> @clear="research"/>
<ElFormItem> </ElFormItem>
<ElButton :icon="elIcons.Search" :loading="searching" native-type="submit" type="primary">搜索</ElButton> </template>
<ElButton :icon="elIcons.Plus" type="primary" @click="addHandler"></ElButton> <template #columns>
</ElFormItem>
</ElForm>
</div>
<ElTable v-loading="searching" :data="tableData"
cell-class-name="table-cell"
class="table-list"
empty-text="暂无数据"
header-row-class-name="table-header"
row-key="id">
<ElTableColumn label="字典标识" prop="dictKey" width="180"/> <ElTableColumn label="字典标识" prop="dictKey" width="180"/>
<ElTableColumn label="字典名称" prop="dictName"/> <ElTableColumn label="字典名称" prop="dictName"/>
<ElTableColumn label="备注" prop="memo"/> <!-- <ElTableColumn label="备注" prop="memo"/> -->
</template>
<ElTableColumn label="操作" width="180"> <DictForm ref="dictForm" :research="research"/>
<template #default="scope"> </ATablePage>
<div class="action-btn">
<ElPopconfirm
cancel-button-text="否"
cancel-button-type="primary"
confirm-button-text="是"
confirm-button-type="danger"
placement="top"
title="是否删除当前数据?"
width="180"
@confirm="delHandler(scope)">
<template #reference>
<ElButton :loading="deling" text type="danger">删除</ElButton>
</template>
</ElPopconfirm>
<ElButton text type="primary" @click="modifyHandler(scope)"></ElButton>
<ElButton text type="primary" @click="selectDictHandle(scope)"></ElButton>
</div>
</template>
</ElTableColumn>
</ElTable>
<ElPagination
v-model:current-page="searchForm.current"
v-model:page-size="searchForm.size"
:hide-on-single-page="false"
:page-sizes="[10, 20, 50, 100, 500]"
:teleported="false"
:total="totalCount"
background layout="total, prev, pager, next, sizes, jumper"
@change="paging"/>
<DictForm ref="dictForm" @edit-succ="paging"/>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import DictApi from '@/pages/sys/dict/dict-api.ts' import DictApi from '@/pages/sys/dict/dict-api.ts'
import { elIcons } from '@/common/element/element.ts'
import DictForm from '@/pages/sys/dict/DictForm.vue' import DictForm from '@/pages/sys/dict/DictForm.vue'
import ATablePage, {
type ATablePageInstance,
buildTablePageProps,
} from '@/components/a-page/a-table-page/ATablePage.tsx'
const totalCount = ref(0) const props = defineProps<{
const tableData = ref<DictTypes.SearchDictResult[]>([]) currentDict?: string
const searchForm = ref<DictTypes.SearchDictParam>({ }>()
orders: 'dict_key,id',
current: 1,
size: 20,
})
const searching = ref(false)
const deling = ref(false)
const dictFormIns = useTemplateRef<InstanceType<typeof DictForm>>('dictForm')
function showDialog(data?: DictTypes.SearchDictResult) {
dictFormIns.value?.open(data)
}
function delHandler({row}: { row: DictTypes.SearchDictResult }) {
deling.value = true
DictApi.del([ row.id! ])
.then(() => {
ElMessage.success('删除成功')
paging()
})
.finally(() => {
deling.value = false
})
}
function modifyHandler({row}: { row: DictTypes.SearchDictResult }) {
showDialog(row)
}
function addHandler() {
showDialog()
}
const emits = defineEmits([ 'searchDict' ]) const emits = defineEmits([ 'searchDict' ])
const dictFormIns = useTemplateRef<InstanceType<typeof DictForm>>('dictForm')
const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage')
function selectDictHandle({row}: { row: DictTypes.SearchDictResult }) { function research() {
emits('searchDict', row) tablePageIns.value?.doSearch()
} }
function reset() { const tablePageProps = buildTablePageProps<DictTypes.SearchDictParam, DictTypes.SearchDictResult>({
searchForm.value = { pageLayout: {
orders: 'dict_key,id', enableHighForm: false,
current: 1, },
size: 20, searchForm: {
paging: DictApi.paging,
},
toolBar: {
leftTools: [
{
icon: 'Plus',
label: '新建',
action() {
dictFormIns.value?.open()
},
},
],
},
table: {
highlightCurrentRow: true,
actionColumn: {
tableActions: [
{
tooltip: '字典项',
icon: 'Postcard',
type: 'primary',
action() {
// emits('searchDict', row)
},
},
{
tooltip: '编辑',
icon: 'Edit',
type: 'success',
action({row}) {
dictFormIns.value?.open(row)
},
},
{
icon: 'Delete',
loading: false,
type: 'danger',
tooltip: '删除',
confirm: {
title: '是否删除当前数据',
},
action({row}) {
return DictApi.del([ row.id! ])
.then(() => {
ElMessage.success('删除成功')
return true
})
},
},
],
},
rowClickHandle(row) {
emits('searchDict', row)
},
},
})
watchEffect(() => {
if (props.currentDict != null) {
tablePageProps.table.currentRowKey = props.currentDict
} }
paging()
}
function paging() {
searching.value = true
DictApi.paging(searchForm.value)
.then(res => {
totalCount.value = res.data?.total ?? 0
tableData.value = res.data?.records ?? []
})
.finally(() => {
searching.value = false
})
}
onMounted(() => {
paging()
}) })
</script> </script>
<style lang="stylus" scoped>
.table-list {
flex 1;
margin 0 0 20px 0
width 100%
:deep(.table-header) {
color #454C59
th {
background-color #EDF1F7
font-weight 500
position relative
& > div {
display flex
gap 5px
align-items center
}
&:not(:first-child) > div::before {
position: absolute;
top: 50%;
left: 1px;
width: 1px;
background-color: #D3D7DE;
transform: translateY(-50%);
content: "";
height 50%
}
}
}
:deep(.table-cell) {
color #2F3540
}
.action-btn {
width 100%
display flex
flex-wrap wrap
& > button {
margin 0
}
}
}
.tool-bar {
display flex
justify-content space-between
margin 0 0 20px 0
}
.pagination {
justify-content: end;
margin: 8px;
}
</style>

View File

@ -1,93 +1,81 @@
<template> <template>
<ElDialog v-model="showDialog" <AFormPanel
:close-on-click-modal="false" ref="formPanel"
destroy-on-close v-bind="formPanelProps">
@close="dialogCloseHandler" <template #default="formData">
width="25vw"> <div class="form-items">
<ElForm :model="dictFormData" <ElFormItem label="字典标识" prop="dictKey">
class="sys_dict-form" <ElInput
> v-model="formData.dictKey"
<ElFormItem label="字典标识"> placeholder="字典标识"/>
<ElInput </ElFormItem>
v-model="dictFormData.dictKey" <ElFormItem label="字典名称" prop="dictName">
placeholder="字典标识"/> <ElInput
</ElFormItem> v-model="formData.dictName"
<ElFormItem label="字典名称"> placeholder="字典名称"/>
<ElInput </ElFormItem>
v-model="dictFormData.dictName" <ElFormItem label="备注">
placeholder="字典名称"/> <ElInput
</ElFormItem> v-model="formData.memo"
<ElFormItem label="备注"> placeholder="备注"/>
<ElInput </ElFormItem>
v-model="dictFormData.memo" </div>
placeholder="备注"/>
</ElFormItem>
</ElForm>
<template #footer>
<ElButton @click="showDialog = false">{{ status === 'view' ? '关闭' : '取消' }}</ElButton>
<ElButton v-if="status !== 'view'" :loading="submiting" type="primary" @click="submitHandler"></ElButton>
</template> </template>
</ElDialog> </AFormPanel>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import DictApi from '@/pages/sys/dict/dict-api.ts' import DictApi from '@/pages/sys/dict/dict-api.ts'
import Strings from '@/common/utils/strings.ts' import Strings from '@/common/utils/strings.ts'
import { ElMessage } from 'element-plus' import AFormPanel, {
type AFormPanelInstance,
buildFormPanelProps,
} from '@/components/a-form-panel/AFormPanel.tsx'
const emits = defineEmits([ 'editSucc' ]) const props = withDefaults(defineProps<{
const showDialog = ref(false) research?: () => void
const submiting = ref(false) }>(), {
const status = ref<'add' | 'view' | 'modify'>('add') research: () => {
const dictFormData = ref<DictTypes.SearchDictResult>({}) },
})
function dialogCloseHandler() { const formPanelIns = useTemplateRef<AFormPanelInstance>('formPanel')
dictFormData.value = {} const status = ref<'add' | 'modify'>('add')
} const formPanelProps = buildFormPanelProps<DictTypes.SearchDictResult>({
detailsLoader(id?: string) {
function submitHandler() { if (Strings.isBlank(id)) {
if (status.value === 'view') return
submiting.value = true
if (dictFormData.value.id != null) {
DictApi.modify(dictFormData.value)
.then(() => {
ElMessage.success('修改成功')
emits('editSucc')
showDialog.value = false
})
.finally(() => {
submiting.value = false
})
} else {
DictApi.add(dictFormData.value)
.then(() => {
ElMessage.success('添加成功')
emits('editSucc')
showDialog.value = false
})
.finally(() => {
submiting.value = false
})
}
}
defineExpose({
open(data: DictTypes.SearchDictResult = {}) {
showDialog.value = true
if (!Strings.isBlank(data.id)) {
status.value = 'modify'
DictApi.detail(data.id!)
.then(res => {
dictFormData.value = res.data
})
} else {
status.value = 'add' status.value = 'add'
dictFormData.value = data return Promise.resolve({})
} else {
status.value = 'modify'
return DictApi
.detail(id!)
.then((res) => {
return res.data
})
} }
}, },
doSubmit(data) {
if (status.value === 'add') {
return DictApi.add(data)
.then(props.research)
} else {
return DictApi.modify(data)
.then(props.research)
}
},
rules: {
dictName: [ {required: true, message: '请填写字典名称', trigger: 'blur'} ],
dictKey: [ {required: true, message: '请填写字典标识', trigger: 'blur'} ],
},
})
watchEffect(() => {
formPanelProps.title = status.value === 'add' ? '新建字典分类' : '修改字典分类'
})
defineExpose({
open(data?: DictTypes.SearchDictResult) {
formPanelIns.value?.open(data?.id)
},
}) })
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
.sys_dict-form {
padding 20px
}
</style> </style>

View File

@ -1,35 +1,24 @@
<template> <template>
<div> <ATablePage
<div class="tool-bar"> ref="tablePage"
<ElForm inline @submit.prevent="paging"> v-bind="tablePageProps">
<ElFormItem> <template #simpleFormItem="formData">
<ElInput <ElFormItem>
v-model="searchForm.dictKey" <ElInput
placeholder="请选择左侧的字典" v-model="formData.dictKey"
readonly/> placeholder="请选择左侧的字典"
</ElFormItem> readonly/>
<ElFormItem> </ElFormItem>
<ElInput <ElFormItem>
v-model="searchForm.txt" <ElInput
clearable v-model="formData.txt"
placeholder="请输入要搜索的文字" clearable
@clear="reset"/> placeholder="请输入要搜索的文字"
</ElFormItem> @clear="research"/>
<ElFormItem> </ElFormItem>
<ElButton :disabled="Strings.isBlank(currentDict.dictKey)" :icon="elIcons.Search" :loading="searching" native-type="submit" type="primary">搜索</ElButton> </template>
<ElButton :disabled="Strings.isBlank(currentDict.dictKey)" :icon="elIcons.Plus" type="primary" @click="addHandler"></ElButton> <template #columns>
</ElFormItem> <!-- <ElTableColumn label="字典标识" prop="dictKey" width="180"/> -->
</ElForm>
</div>
<ElTable v-loading="searching" :data="tableData"
cell-class-name="table-cell"
class="table-list"
empty-text="暂无数据"
header-row-class-name="table-header"
row-key="id">
<!-- <ElTableColumn label="字典 Id" prop="dictId"/> -->
<ElTableColumn label="字典标识" prop="dictKey" width="180"/>
<ElTableColumn label="值" prop="val"/> <ElTableColumn label="值" prop="val"/>
@ -37,155 +26,104 @@
<ElTableColumn label="排序" prop="sort"/> <ElTableColumn label="排序" prop="sort"/>
<ElTableColumn label="备注" prop="memo"/> <!-- <ElTableColumn label="备注" prop="memo"/> -->
</template>
<ElTableColumn label="操作" width="180"> <DictItemForm ref="dictItemForm" :dict-id="currentDict.id" :dict-key="currentDict.dictKey" :research="research"/>
<template #default="scope"> </ATablePage>
<div class="action-btn">
<ElPopconfirm
cancel-button-text="否"
cancel-button-type="primary"
confirm-button-text="是"
confirm-button-type="danger"
placement="top"
title="是否删除当前数据?"
width="180"
@confirm="delHandler(scope)">
<template #reference>
<ElButton :loading="deling" text type="danger">删除</ElButton>
</template>
</ElPopconfirm>
<ElButton text type="primary" @click="modifyHandler(scope)"></ElButton>
</div>
</template>
</ElTableColumn>
</ElTable>
<DictItemForm ref="dictItemForm" @edit-succ="paging"/>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import DictApi from '@/pages/sys/dict/dict-api.ts' import DictApi from '@/pages/sys/dict/dict-api.ts'
import DictItemApi from '@/pages/sys/dict/dict-item-api.ts' import DictItemApi from '@/pages/sys/dict/dict-item-api.ts'
import { elIcons } from '@/common/element/element.ts'
import DictItemForm from '@/pages/sys/dict/DictItemForm.vue' import DictItemForm from '@/pages/sys/dict/DictItemForm.vue'
import Strings from '@/common/utils/strings.ts' import ATablePage, {
type ATablePageInstance,
buildTablePageProps,
} from '@/components/a-page/a-table-page/ATablePage.tsx'
const tableData = ref<DictItemTypes.SearchDictItemResult[]>([])
const searchForm = reactive<DictItemTypes.SearchDictItemParam>({
current: 1,
size: 20,
})
const searching = ref(false)
const deling = ref(false)
const currentDict = reactive<DictTypes.SearchDictResult>({})
const dictItemFormIns = useTemplateRef<InstanceType<typeof DictItemForm>>('dictItemForm') const dictItemFormIns = useTemplateRef<InstanceType<typeof DictItemForm>>('dictItemForm')
const tablePageIns = useTemplateRef<ATablePageInstance<DictItemTypes.SearchDictItemParam, DictItemTypes.SearchDictItemResult>>('tablePage')
const currentDict = reactive<DictTypes.SearchDictResult>({})
function showDialog(data?: any) { function research() {
dictItemFormIns.value?.open(data) tablePageIns.value?.doSearch()
} }
function delHandler({row}: { row: DictItemTypes.SearchDictItemResult }) { const tablePageProps = buildTablePageProps<DictItemTypes.SearchDictItemParam, DictItemTypes.SearchDictItemResult>({
deling.value = true pageLayout: {
DictItemApi.del([ row.id! ]) enableHighForm: false,
.then(() => { },
ElMessage.success('删除成功') searchForm: {
paging() list(param) {
}) if (currentDict.dictKey == null) {
.finally(() => { return Promise.resolve({
deling.value = false code: 0, msg: '',
}) message: '',
} success: false,
data: [],
})
}
return DictApi.obtainDictData({...param, dictKey: currentDict.dictKey})
},
resetForm() {
return {dictId: currentDict.id, dictKey: currentDict.dictKey}
},
simpleForm: {
colCount: 2,
},
},
toolBar: {
leftTools: [
{
icon: 'Plus',
label: '新建',
action() {
dictItemFormIns.value?.open({dictId: currentDict.id, dictKey: currentDict.dictKey})
},
},
],
},
table: {
actionColumn: {
tableActions: [
{
tooltip: '编辑',
icon: 'Edit',
type: 'success',
action({row}) {
dictItemFormIns.value?.open(row)
},
},
{
icon: 'Delete',
loading: false,
type: 'danger',
tooltip: '删除',
confirm: {
title: '是否删除当前数据',
},
action({row}) {
return DictItemApi.del([ row.id! ])
.then(() => {
ElMessage.success('删除成功')
return true
})
},
},
],
},
},
function modifyHandler({row}: { row: DictItemTypes.SearchDictItemResult }) { })
showDialog(row)
}
function addHandler() { watchEffect(() => {
if (Strings.isBlank(searchForm.dictKey)) { tablePageProps.toolBar.leftTools![0].disabled = currentDict.dictKey == null
ElMessage.error('请选择左侧的字典') })
return
}
showDialog({dictId: currentDict.id, dictKey: currentDict.dictKey})
}
function reset() {
searchForm.txt = ''
paging()
}
function paging() {
if (Strings.isBlank(searchForm.dictKey)) {
ElMessage.error('请选择左侧的字典')
return
}
searching.value = true
DictApi.obtainDictData(searchForm)
.then(res => {
tableData.value = res.data ?? []
})
.finally(() => {
searching.value = false
})
}
defineExpose({ defineExpose({
reload(data: DictTypes.SearchDictResult) { reload(data: DictTypes.SearchDictResult) {
searchForm.dictKey = data.dictKey
Object.assign(currentDict, data) Object.assign(currentDict, data)
paging() tablePageIns.value?.formData.$reset({dictId: currentDict.id, dictKey: currentDict.dictKey})
research()
}, },
}) })
</script> </script>
<style lang="stylus" scoped>
.table-list {
flex 1;
width 100%
:deep(.table-header) {
color #454C59
th {
background-color #EDF1F7
font-weight 500
position relative
& > div {
display flex
gap 5px
align-items center
}
&:not(:first-child) > div::before {
position: absolute;
top: 50%;
left: 1px;
width: 1px;
background-color: #D3D7DE;
transform: translateY(-50%);
content: "";
height 50%
}
}
}
:deep(.table-cell) {
color #2F3540
}
.action-btn {
width 100%
display flex
flex-wrap wrap
& > button {
margin 0
}
}
}
.tool-bar {
display flex
justify-content space-between
margin 0 0 20px 0
}
</style>

View File

@ -1,110 +1,95 @@
<template> <template>
<ElDialog v-model="showDialog" <AFormPanel
:close-on-click-modal="false" ref="formPanel"
destroy-on-close v-bind="formPanelProps">
@close="dialogCloseHandler" <template #default="formData">
width="25vw"> <div class="form-items">
<ElForm :model="dictItemFormData" <ElFormItem label="值" prop="val">
class="sys_dict_item-form" <ElInput
> v-model="formData.val"
<ElFormItem label="值"> placeholder="值"/>
<ElInput </ElFormItem>
v-model="dictItemFormData.val" <ElFormItem label="文本" prop="txt">
<ElInput
placeholder="值"/> v-model="formData.txt"
</ElFormItem> placeholder="文本"/>
<ElFormItem label="文本"> </ElFormItem>
<ElInput <ElFormItem label="排序" prop="sort">
v-model="dictItemFormData.txt" <ElInputNumber
v-model="formData.sort"
placeholder="文本"/> :min="1"
</ElFormItem> placeholder="排序"
<ElFormItem label="排序"> style="width: 100%"/>
<ElInputNumber </ElFormItem>
v-model="dictItemFormData.sort" <!-- <ElFormItem label="备注">
<ElInput
:min="1" v-model="formData.memo"
placeholder="排序" placeholder="备注"/>
style="width: 100%"/> </ElFormItem> -->
</ElFormItem> </div>
<ElFormItem label="备注">
<ElInput
v-model="dictItemFormData.memo"
placeholder="备注"/>
</ElFormItem>
</ElForm>
<template #footer>
<ElButton @click="showDialog = false">{{ status === 'view' ? '关闭' : '取消' }}</ElButton>
<ElButton v-if="status !== 'view'" :loading="submiting" type="primary" @click="submitHandler"></ElButton>
</template> </template>
</ElDialog> </AFormPanel>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import DictItemApi from '@/pages/sys/dict/dict-item-api.ts' import DictItemApi from '@/pages/sys/dict/dict-item-api.ts'
import Strings from '@/common/utils/strings.ts' import Strings from '@/common/utils/strings.ts'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import AFormPanel, {
type AFormPanelInstance,
buildFormPanelProps,
} from '@/components/a-form-panel/AFormPanel.tsx'
const emits = defineEmits([ 'editSucc' ]) const props = withDefaults(defineProps<{
const showDialog = ref(false) research?: () => void
const submiting = ref(false) // Id
const status = ref<'add' | 'view' | 'modify'>('add') dictId?: string
// const dictItemFormData = ref<DictItemTypes.SearchDictItemResult>({}) //
const dictItemFormData = ref<any>({}) dictKey?: string
}>(), {
function dialogCloseHandler() { research: () => {
dictItemFormData.value = {} },
} })
const status = ref<'add' | 'modify'>('add')
function submitHandler() { const formPanelIns = useTemplateRef<AFormPanelInstance>('formPanel')
if (status.value === 'view') return const formPanelProps = buildFormPanelProps<DictItemTypes.SearchDictItemResult>({
submiting.value = true detailsLoader(id?: string) {
if (dictItemFormData.value.id != null) { if (Strings.isBlank(id)) {
DictItemApi.modify(dictItemFormData.value)
.then(() => {
ElMessage.success('修改成功')
emits('editSucc')
showDialog.value = false
})
.finally(() => {
submiting.value = false
})
} else {
if (Strings.isBlank(dictItemFormData.value.dictId) || Strings.isBlank(dictItemFormData.value.dictKey)) {
ElMessage.error('未指定字典')
return
}
DictItemApi.add(dictItemFormData.value)
.then(() => {
ElMessage.success('添加成功')
emits('editSucc')
showDialog.value = false
})
.finally(() => {
submiting.value = false
})
}
}
defineExpose({
// open(data: DictItemTypes.SearchDictItemResult = {}) {
open(data: any = {}) {
showDialog.value = true
if (!Strings.isBlank(data.id)) {
status.value = 'modify'
DictItemApi.detail(data.id!)
.then(res => {
dictItemFormData.value = res.data
})
} else {
status.value = 'add' status.value = 'add'
dictItemFormData.value = data return Promise.resolve()
} else {
status.value = 'modify'
return DictItemApi
.detail(id!)
.then((res) => {
return res.data
})
} }
}, },
doSubmit(data) {
if (status.value === 'add') {
if (Strings.isBlank(props.dictId) || Strings.isBlank(props.dictKey)) {
ElMessage.error('未指定字典')
return Promise.reject()
}
return DictItemApi.add({...data, dictKey: props.dictKey, dictId: props.dictId})
.then(props.research)
} else {
return DictItemApi.modify(data)
.then(props.research)
}
},
rules: {
val: [ {required: true, message: '请填写值', trigger: 'blur'} ],
txt: [ {required: true, message: '请填写文本', trigger: 'blur'} ],
sort: [ {required: true, message: '请填写排序', trigger: 'blur'} ],
},
})
watchEffect(() => {
formPanelProps.title = status.value === 'add' ? '新建字典项' : '修改字典项'
})
defineExpose({
open(data: any = {}) {
formPanelIns.value?.open(data?.id)
},
}) })
</script> </script>
<style lang="stylus" scoped>
.sys_dict_item-form {
padding 20px
}
</style>

View File

@ -8,9 +8,9 @@
<ElTreeSelect v-model="formData.pid" v-loading="loadingMenus" :data="menuTreeDataSource" :default-expanded-keys="['0']" :render-after-expand="false" check-strictly placeholder="选择上级菜单"/> <ElTreeSelect v-model="formData.pid" v-loading="loadingMenus" :data="menuTreeDataSource" :default-expanded-keys="['0']" :render-after-expand="false" check-strictly placeholder="选择上级菜单"/>
</ElFormItem> </ElFormItem>
<ElFormItem label="客户端" prop="clients"> <ElFormItem label="客户端" prop="clients">
<ElCheckboxGroup v-model="formData.clients"> <ElRadioGroup v-model="formData.clientCode">
<ElCheckbox v-for="client in ClientUtil.clients" :key="client.val" :label="client.txt" :value="client.val"/> <ElRadio v-for="client in ClientUtil.clients" :key="client.val" :label="client.txt" :value="client.val"/>
</ElCheckboxGroup> </ElRadioGroup>
</ElFormItem> </ElFormItem>
<ElFormItem label="类型"> <ElFormItem label="类型">
<ElSelect v-model="formData.menuCategory" :data="menuCategoryData" placeholder="选择类型"> <ElSelect v-model="formData.menuCategory" :data="menuCategoryData" placeholder="选择类型">
@ -87,7 +87,7 @@ const formPanelProps = buildFormPanelProps<MenuTypes.MenuForm>({
return Promise.resolve({ return Promise.resolve({
icon: 'dianzixiaopiao', icon: 'dianzixiaopiao',
pid: '0', pid: '0',
clients: [ 1 ], clientCode: 1,
menuCategory: MenuCategory.Catalog, menuCategory: MenuCategory.Catalog,
sn: nanoid(10), sn: nanoid(10),
} as MenuTypes.MenuForm) } as MenuTypes.MenuForm)
@ -98,7 +98,6 @@ const formPanelProps = buildFormPanelProps<MenuTypes.MenuForm>({
.then((res) => { .then((res) => {
return { return {
...res.data, ...res.data,
clients: ClientUtil.getClients(res.data.clientCode!).map(it => it.val),
} as MenuTypes.MenuForm } as MenuTypes.MenuForm
}) })
} }

View File

@ -72,6 +72,7 @@ declare global {
// 路由名称 // 路由名称
routeName?: string routeName?: string
routePath?: string routePath?: string
clientCode?: number
menuCategory?: MenuCategory menuCategory?: MenuCategory
} }
@ -89,6 +90,7 @@ declare global {
// 路由名称 // 路由名称
routeName?: string routeName?: string
routePath?: string routePath?: string
clientCode?: number
menuCategory?: MenuCategory menuCategory?: MenuCategory
} }
} }