chore:字典组件优化,支持数组格式,其他模块代码优化

This commit is contained in:
AN 2025-04-19 08:18:07 +08:00
parent 8b1be45eb5
commit 8c5ea2ae72
10 changed files with 138 additions and 43 deletions

View File

@ -50,6 +50,7 @@
"@sa/hooks": "workspace:*",
"@sa/materials": "workspace:*",
"@sa/utils": "workspace:*",
"@tinymce/tinymce-vue": "^6.1.0",
"@vueuse/core": "12.5.0",
"clipboard": "2.0.11",
"dayjs": "1.11.13",

View File

@ -8,14 +8,16 @@ defineOptions({ name: 'DictSelect' });
interface Props {
dictCode: string;
immediate?: boolean;
multiple?: boolean;
[key: string]: any;
}
const props = withDefaults(defineProps<Props>(), {
immediate: false
immediate: false,
multiple: false
});
const value = defineModel<string | null>('value', { required: false });
const value = defineModel<string | string[] | null>('value', { required: false });
const attrs: SelectProps = useAttrs();
const { options } = useDict(props.dictCode, props.immediate);
@ -24,6 +26,7 @@ const { options } = useDict(props.dictCode, props.immediate);
<template>
<NSelect
v-model:value="value"
:multiple="multiple"
:loading="!options.length"
:options="options"
:clear-filter-after-select="false"

View File

@ -7,10 +7,10 @@ import { isNotNull } from '@/utils/common';
defineOptions({ name: 'DictTag' });
interface Props {
value?: string | number;
value?: string[] | number[] | string | number;
dictCode?: string;
immediate?: boolean;
dictData?: Api.System.DictData;
dictData?: Api.System.DictData[];
[key: string]: any;
}
@ -18,29 +18,38 @@ const props = withDefaults(defineProps<Props>(), {
immediate: false,
dictData: undefined,
dictCode: '',
value: ''
value: () => []
});
const attrs = useAttrs() as TagProps;
const dictTagData = computed(() => {
const dictTagData = computed<Api.System.DictData[]>(() => {
if (props.dictData) {
return props.dictData;
}
// props.value 0
if (props.dictCode && isNotNull(props.value)) {
const { transformDictData } = useDict(props.dictCode, props.immediate);
return transformDictData(String(props.value));
return transformDictData(props.value) || [];
}
return null;
return [];
});
</script>
<template>
<NTag v-if="dictTagData" :class="dictTagData.cssClass" :type="dictTagData.listClass" v-bind="attrs">
{{ dictTagData.dictLabel }}
</NTag>
<div v-if="dictTagData.length">
<NTag
v-for="item in dictTagData"
:key="item.dictValue"
class="mb-2 mr-2"
:class="[item.cssClass]"
:type="item.listClass"
v-bind="attrs"
>
{{ item.dictLabel }}
</NTag>
</div>
</template>
<style scoped></style>

View File

@ -51,20 +51,19 @@ const jsonData = computed(() => {
<template>
<div class="json-preview">
<template v-if="jsonData">
<VueJsonPretty
:data="jsonData"
:deep="deep"
:show-double-quotes="showDoubleQuotes"
:show-length="showLength"
:show-line="showLine"
:show-line-number="showLineNumber"
:show-icon="showIcon"
:show-select-controller="showSelectController"
:collapsed-level="collapsedLevel"
:highlight-mouseover-node="highlightMouseoverNode"
/>
</template>
<VueJsonPretty
v-if="jsonData"
:data="jsonData"
:deep="deep"
:show-double-quotes="showDoubleQuotes"
:show-length="showLength"
:show-line="showLine"
:show-line-number="showLineNumber"
:show-icon="showIcon"
:show-select-controller="showSelectController"
:collapsed-level="collapsedLevel"
:highlight-mouseover-node="highlightMouseoverNode"
/>
<span v-else-if="props.data">{{ props.data }}</span>
<div v-else class="empty-data">暂无数据</div>
</div>

View File

@ -2,7 +2,7 @@ import { ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { fetchGetDictDataByType } from '@/service/api/system';
import { useDictStore } from '@/store/modules/dict';
import { isNull } from '@/utils/common';
export function useDict(dictType: string, immediate: boolean = true) {
const dictStore = useDictStore();
const { dictData: dictList } = storeToRefs(dictStore);
@ -40,9 +40,12 @@ export function useDict(dictType: string, immediate: boolean = true) {
options.value = data.value.map(dict => ({ label: dict.dictLabel!, value: dict.dictValue! }));
}
function transformDictData(dictValue: string): Api.System.DictData | undefined {
if (!data.value.length || !dictValue) return undefined;
return data.value.find(dict => dict.dictValue === dictValue);
function transformDictData(dictValue: string[] | number[] | string | number) {
if (!data.value.length || isNull(dictValue)) return undefined;
if (Array.isArray(dictValue)) {
return data.value.filter(dict => dictValue.some(value => dict.dictValue === value.toString()));
}
return data.value.filter(dict => dict.dictValue === dictValue.toString());
}
if (immediate) {

View File

@ -511,5 +511,88 @@ declare namespace Api {
/** tenant package select list */
type TenantPackageSelectList = Common.CommonRecord<Pick<TenantPackage, 'packageId' | 'packageName'>>;
/** notice */
type Notice = Common.CommonRecord<{
/** 公告ID */
noticeId: CommonType.IdType;
/** 租户编号 */
tenantId: CommonType.IdType;
/** 公告标题 */
noticeTitle: string;
/** 公告类型 */
noticeType: string;
/** 公告内容 */
noticeContent: string;
/** 公告状态 */
status: string;
/** 创建者 */
createByName: string;
/** 备注 */
remark: string;
}>;
/** notice search params */
type NoticeSearchParams = CommonType.RecordNullable<
Pick<Api.System.Notice, 'noticeTitle' | 'noticeType'> & Api.Common.CommonSearchParams
>;
/** notice operate params */
type NoticeOperateParams = CommonType.RecordNullable<
Pick<Api.System.Notice, 'noticeId' | 'noticeTitle' | 'noticeType' | 'noticeContent' | 'status'>
>;
/** notice list */
type NoticeList = Api.Common.PaginatingQueryRecord<Notice>;
/** client */
type Client = Common.CommonRecord<{
/** id */
id: CommonType.IdType;
/** 客户端id */
clientId: string;
/** 客户端key */
clientKey: string;
/** 客户端秘钥 */
clientSecret: string;
/** 授权类型 */
grantType: string;
/** 授权类型列表 */
grantTypeList: string[];
/** 设备类型 */
deviceType: string;
/** token活跃超时时间 */
activeTimeout: number;
/** token固定超时 */
timeout: number;
/** 状态 */
status: string;
/** 删除标志0代表存在 1代表删除 */
delFlag: string;
}>;
/** client search params */
type ClientSearchParams = CommonType.RecordNullable<
Pick<Api.System.Client, 'clientKey' | 'clientSecret' | 'status'> & Api.Common.CommonSearchParams
>;
/** client operate params */
type ClientOperateParams = CommonType.RecordNullable<
Pick<
Api.System.Client,
| 'id'
| 'clientId'
| 'clientKey'
| 'clientSecret'
| 'grantTypeList'
| 'deviceType'
| 'activeTimeout'
| 'timeout'
| 'status'
>
>;
/** client list */
type ClientList = Api.Common.PaginatingQueryRecord<Client>;
}
}

View File

@ -80,6 +80,11 @@ export function isNotNull(value: any) {
return value !== undefined && value !== null && value !== '' && value !== 'undefined' && value !== 'null';
}
/** 判断是否为空 */
export function isNull(value: any) {
return value === undefined || value === null || value === '' || value === 'undefined' || value === 'null';
}
/**
*
*

View File

@ -31,8 +31,8 @@ function closeDrawer() {
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
<NDrawerContent :title="title" :native-scrollbar="false" closable>
<NDescriptions label-placement="left" :column="1" size="small" bordered>
<NDescriptionsItem label="用户账号">
{{ props.rowData?.userName }}
<NDescriptionsItem label="账号信息">
{{ props.rowData?.userName }} | {{ props.rowData?.ipaddr }} | {{ props.rowData?.loginLocation }}
</NDescriptionsItem>
<NDescriptionsItem label="客户端">
{{ props.rowData?.clientKey }}
@ -40,12 +40,6 @@ function closeDrawer() {
<NDescriptionsItem label="设备类型">
<DictTag size="small" :value="props.rowData?.deviceType" dict-code="sys_device_type" />
</NDescriptionsItem>
<NDescriptionsItem label="登录IP地址">
{{ props.rowData?.ipaddr }}
</NDescriptionsItem>
<NDescriptionsItem label="登录地点">
{{ props.rowData?.loginLocation }}
</NDescriptionsItem>
<NDescriptionsItem label="浏览器类型">
<div class="flex items-center gap-2">
<SvgIcon :icon="getBrowserIcon(props.rowData?.browser ?? '')" />

View File

@ -10,6 +10,7 @@ import { useAppStore } from '@/store/modules/app';
import { useDownload } from '@/hooks/business/download';
import { useTable, useTableOperate } from '@/hooks/common/table';
import DictTag from '@/components/custom/dict-tag.vue';
import { useDict } from '@/hooks/business/dict';
import PostOperateDrawer from './modules/post-operate-drawer.vue';
import PostSearch from './modules/post-search.vue';
@ -17,6 +18,7 @@ defineOptions({
name: 'PostList'
});
useDict('sys_normal_disable');
const appStore = useAppStore();
const { download } = useDownload();
const { hasAuth } = useAuth();
@ -170,7 +172,6 @@ async function handleExport() {
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
const deptPattern = ref<string>();
const deptData = ref<Api.Common.CommonTreeRecord>([]);
const selectedKeys = ref<string[]>([]);
async function getTreeData() {
startTreeLoading();
@ -184,7 +185,6 @@ async function getTreeData() {
getTreeData();
function handleClickTree(keys: string[]) {
selectedKeys.value = keys;
searchParams.belongDeptId = keys.length ? keys[0] : null;
checkedRowKeys.value = [];
getDataByPage();
@ -192,7 +192,6 @@ function handleClickTree(keys: string[]) {
function handleResetTreeData() {
deptPattern.value = undefined;
selectedKeys.value = [];
searchParams.belongDeptId = null;
getTreeData();
getDataByPage();
@ -212,7 +211,6 @@ function handleResetTreeData() {
<NInput v-model:value="deptPattern" clearable :placeholder="$t('common.keywordSearch')" />
<NSpin class="dept-tree" :show="treeLoading">
<NTree
v-model:selected-keys="selectedKeys"
block-node
show-line
:data="deptData as []"

View File

@ -261,7 +261,7 @@ watch(visible, () => {
class="w-full"
/>
</NFormItem>
<NFormItem label="用户数量" path="accountCount">
<NFormItem path="accountCount">
<template #label>
<div class="flex-center">
<FormTip content="-1不限制用户数量" />
@ -270,7 +270,7 @@ watch(visible, () => {
</template>
<NInputNumber v-model:value="model.accountCount" placeholder="请输入用户数量" min="-1" class="w-full" />
</NFormItem>
<NFormItem label="绑定域名" path="domain">
<NFormItem path="domain">
<template #label>
<div class="flex-center">
<FormTip