chore:字典组件优化,支持数组格式,其他模块代码优化
This commit is contained in:
parent
8b1be45eb5
commit
8c5ea2ae72
@ -50,6 +50,7 @@
|
|||||||
"@sa/hooks": "workspace:*",
|
"@sa/hooks": "workspace:*",
|
||||||
"@sa/materials": "workspace:*",
|
"@sa/materials": "workspace:*",
|
||||||
"@sa/utils": "workspace:*",
|
"@sa/utils": "workspace:*",
|
||||||
|
"@tinymce/tinymce-vue": "^6.1.0",
|
||||||
"@vueuse/core": "12.5.0",
|
"@vueuse/core": "12.5.0",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
"dayjs": "1.11.13",
|
"dayjs": "1.11.13",
|
||||||
|
@ -8,14 +8,16 @@ defineOptions({ name: 'DictSelect' });
|
|||||||
interface Props {
|
interface Props {
|
||||||
dictCode: string;
|
dictCode: string;
|
||||||
immediate?: boolean;
|
immediate?: boolean;
|
||||||
|
multiple?: boolean;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
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 attrs: SelectProps = useAttrs();
|
||||||
const { options } = useDict(props.dictCode, props.immediate);
|
const { options } = useDict(props.dictCode, props.immediate);
|
||||||
@ -24,6 +26,7 @@ const { options } = useDict(props.dictCode, props.immediate);
|
|||||||
<template>
|
<template>
|
||||||
<NSelect
|
<NSelect
|
||||||
v-model:value="value"
|
v-model:value="value"
|
||||||
|
:multiple="multiple"
|
||||||
:loading="!options.length"
|
:loading="!options.length"
|
||||||
:options="options"
|
:options="options"
|
||||||
:clear-filter-after-select="false"
|
:clear-filter-after-select="false"
|
||||||
|
@ -7,10 +7,10 @@ import { isNotNull } from '@/utils/common';
|
|||||||
defineOptions({ name: 'DictTag' });
|
defineOptions({ name: 'DictTag' });
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
value?: string | number;
|
value?: string[] | number[] | string | number;
|
||||||
dictCode?: string;
|
dictCode?: string;
|
||||||
immediate?: boolean;
|
immediate?: boolean;
|
||||||
dictData?: Api.System.DictData;
|
dictData?: Api.System.DictData[];
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,29 +18,38 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
immediate: false,
|
immediate: false,
|
||||||
dictData: undefined,
|
dictData: undefined,
|
||||||
dictCode: '',
|
dictCode: '',
|
||||||
value: ''
|
value: () => []
|
||||||
});
|
});
|
||||||
|
|
||||||
const attrs = useAttrs() as TagProps;
|
const attrs = useAttrs() as TagProps;
|
||||||
|
|
||||||
const dictTagData = computed(() => {
|
const dictTagData = computed<Api.System.DictData[]>(() => {
|
||||||
if (props.dictData) {
|
if (props.dictData) {
|
||||||
return props.dictData;
|
return props.dictData;
|
||||||
}
|
}
|
||||||
// 避免 props.value 为 0 时,无法触发
|
// 避免 props.value 为 0 时,无法触发
|
||||||
if (props.dictCode && isNotNull(props.value)) {
|
if (props.dictCode && isNotNull(props.value)) {
|
||||||
const { transformDictData } = useDict(props.dictCode, props.immediate);
|
const { transformDictData } = useDict(props.dictCode, props.immediate);
|
||||||
return transformDictData(String(props.value));
|
return transformDictData(props.value) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return [];
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NTag v-if="dictTagData" :class="dictTagData.cssClass" :type="dictTagData.listClass" v-bind="attrs">
|
<div v-if="dictTagData.length">
|
||||||
{{ dictTagData.dictLabel }}
|
<NTag
|
||||||
</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>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
@ -51,20 +51,19 @@ const jsonData = computed(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="json-preview">
|
<div class="json-preview">
|
||||||
<template v-if="jsonData">
|
<VueJsonPretty
|
||||||
<VueJsonPretty
|
v-if="jsonData"
|
||||||
:data="jsonData"
|
:data="jsonData"
|
||||||
:deep="deep"
|
:deep="deep"
|
||||||
:show-double-quotes="showDoubleQuotes"
|
:show-double-quotes="showDoubleQuotes"
|
||||||
:show-length="showLength"
|
:show-length="showLength"
|
||||||
:show-line="showLine"
|
:show-line="showLine"
|
||||||
:show-line-number="showLineNumber"
|
:show-line-number="showLineNumber"
|
||||||
:show-icon="showIcon"
|
:show-icon="showIcon"
|
||||||
:show-select-controller="showSelectController"
|
:show-select-controller="showSelectController"
|
||||||
:collapsed-level="collapsedLevel"
|
:collapsed-level="collapsedLevel"
|
||||||
:highlight-mouseover-node="highlightMouseoverNode"
|
:highlight-mouseover-node="highlightMouseoverNode"
|
||||||
/>
|
/>
|
||||||
</template>
|
|
||||||
<span v-else-if="props.data">{{ props.data }}</span>
|
<span v-else-if="props.data">{{ props.data }}</span>
|
||||||
<div v-else class="empty-data">暂无数据</div>
|
<div v-else class="empty-data">暂无数据</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@ import { ref, watch } from 'vue';
|
|||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { fetchGetDictDataByType } from '@/service/api/system';
|
import { fetchGetDictDataByType } from '@/service/api/system';
|
||||||
import { useDictStore } from '@/store/modules/dict';
|
import { useDictStore } from '@/store/modules/dict';
|
||||||
|
import { isNull } from '@/utils/common';
|
||||||
export function useDict(dictType: string, immediate: boolean = true) {
|
export function useDict(dictType: string, immediate: boolean = true) {
|
||||||
const dictStore = useDictStore();
|
const dictStore = useDictStore();
|
||||||
const { dictData: dictList } = storeToRefs(dictStore);
|
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! }));
|
options.value = data.value.map(dict => ({ label: dict.dictLabel!, value: dict.dictValue! }));
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformDictData(dictValue: string): Api.System.DictData | undefined {
|
function transformDictData(dictValue: string[] | number[] | string | number) {
|
||||||
if (!data.value.length || !dictValue) return undefined;
|
if (!data.value.length || isNull(dictValue)) return undefined;
|
||||||
return data.value.find(dict => dict.dictValue === dictValue);
|
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) {
|
if (immediate) {
|
||||||
|
83
src/typings/api/system.api.d.ts
vendored
83
src/typings/api/system.api.d.ts
vendored
@ -511,5 +511,88 @@ declare namespace Api {
|
|||||||
|
|
||||||
/** tenant package select list */
|
/** tenant package select list */
|
||||||
type TenantPackageSelectList = Common.CommonRecord<Pick<TenantPackage, 'packageId' | 'packageName'>>;
|
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>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,11 @@ export function isNotNull(value: any) {
|
|||||||
return value !== undefined && value !== null && value !== '' && value !== 'undefined' && value !== 'null';
|
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';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造树型结构数据
|
* 构造树型结构数据
|
||||||
*
|
*
|
||||||
|
@ -31,8 +31,8 @@ function closeDrawer() {
|
|||||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
||||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||||
<NDescriptions label-placement="left" :column="1" size="small" bordered>
|
<NDescriptions label-placement="left" :column="1" size="small" bordered>
|
||||||
<NDescriptionsItem label="用户账号">
|
<NDescriptionsItem label="账号信息">
|
||||||
{{ props.rowData?.userName }}
|
{{ props.rowData?.userName }} | {{ props.rowData?.ipaddr }} | {{ props.rowData?.loginLocation }}
|
||||||
</NDescriptionsItem>
|
</NDescriptionsItem>
|
||||||
<NDescriptionsItem label="客户端">
|
<NDescriptionsItem label="客户端">
|
||||||
{{ props.rowData?.clientKey }}
|
{{ props.rowData?.clientKey }}
|
||||||
@ -40,12 +40,6 @@ function closeDrawer() {
|
|||||||
<NDescriptionsItem label="设备类型">
|
<NDescriptionsItem label="设备类型">
|
||||||
<DictTag size="small" :value="props.rowData?.deviceType" dict-code="sys_device_type" />
|
<DictTag size="small" :value="props.rowData?.deviceType" dict-code="sys_device_type" />
|
||||||
</NDescriptionsItem>
|
</NDescriptionsItem>
|
||||||
<NDescriptionsItem label="登录IP地址">
|
|
||||||
{{ props.rowData?.ipaddr }}
|
|
||||||
</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="登录地点">
|
|
||||||
{{ props.rowData?.loginLocation }}
|
|
||||||
</NDescriptionsItem>
|
|
||||||
<NDescriptionsItem label="浏览器类型">
|
<NDescriptionsItem label="浏览器类型">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<SvgIcon :icon="getBrowserIcon(props.rowData?.browser ?? '')" />
|
<SvgIcon :icon="getBrowserIcon(props.rowData?.browser ?? '')" />
|
||||||
|
@ -10,6 +10,7 @@ import { useAppStore } from '@/store/modules/app';
|
|||||||
import { useDownload } from '@/hooks/business/download';
|
import { useDownload } from '@/hooks/business/download';
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
import { useTable, useTableOperate } from '@/hooks/common/table';
|
||||||
import DictTag from '@/components/custom/dict-tag.vue';
|
import DictTag from '@/components/custom/dict-tag.vue';
|
||||||
|
import { useDict } from '@/hooks/business/dict';
|
||||||
import PostOperateDrawer from './modules/post-operate-drawer.vue';
|
import PostOperateDrawer from './modules/post-operate-drawer.vue';
|
||||||
import PostSearch from './modules/post-search.vue';
|
import PostSearch from './modules/post-search.vue';
|
||||||
|
|
||||||
@ -17,6 +18,7 @@ defineOptions({
|
|||||||
name: 'PostList'
|
name: 'PostList'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useDict('sys_normal_disable');
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const { download } = useDownload();
|
const { download } = useDownload();
|
||||||
const { hasAuth } = useAuth();
|
const { hasAuth } = useAuth();
|
||||||
@ -170,7 +172,6 @@ async function handleExport() {
|
|||||||
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
|
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
|
||||||
const deptPattern = ref<string>();
|
const deptPattern = ref<string>();
|
||||||
const deptData = ref<Api.Common.CommonTreeRecord>([]);
|
const deptData = ref<Api.Common.CommonTreeRecord>([]);
|
||||||
const selectedKeys = ref<string[]>([]);
|
|
||||||
|
|
||||||
async function getTreeData() {
|
async function getTreeData() {
|
||||||
startTreeLoading();
|
startTreeLoading();
|
||||||
@ -184,7 +185,6 @@ async function getTreeData() {
|
|||||||
getTreeData();
|
getTreeData();
|
||||||
|
|
||||||
function handleClickTree(keys: string[]) {
|
function handleClickTree(keys: string[]) {
|
||||||
selectedKeys.value = keys;
|
|
||||||
searchParams.belongDeptId = keys.length ? keys[0] : null;
|
searchParams.belongDeptId = keys.length ? keys[0] : null;
|
||||||
checkedRowKeys.value = [];
|
checkedRowKeys.value = [];
|
||||||
getDataByPage();
|
getDataByPage();
|
||||||
@ -192,7 +192,6 @@ function handleClickTree(keys: string[]) {
|
|||||||
|
|
||||||
function handleResetTreeData() {
|
function handleResetTreeData() {
|
||||||
deptPattern.value = undefined;
|
deptPattern.value = undefined;
|
||||||
selectedKeys.value = [];
|
|
||||||
searchParams.belongDeptId = null;
|
searchParams.belongDeptId = null;
|
||||||
getTreeData();
|
getTreeData();
|
||||||
getDataByPage();
|
getDataByPage();
|
||||||
@ -212,7 +211,6 @@ function handleResetTreeData() {
|
|||||||
<NInput v-model:value="deptPattern" clearable :placeholder="$t('common.keywordSearch')" />
|
<NInput v-model:value="deptPattern" clearable :placeholder="$t('common.keywordSearch')" />
|
||||||
<NSpin class="dept-tree" :show="treeLoading">
|
<NSpin class="dept-tree" :show="treeLoading">
|
||||||
<NTree
|
<NTree
|
||||||
v-model:selected-keys="selectedKeys"
|
|
||||||
block-node
|
block-node
|
||||||
show-line
|
show-line
|
||||||
:data="deptData as []"
|
:data="deptData as []"
|
||||||
|
@ -261,7 +261,7 @@ watch(visible, () => {
|
|||||||
class="w-full"
|
class="w-full"
|
||||||
/>
|
/>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
<NFormItem label="用户数量" path="accountCount">
|
<NFormItem path="accountCount">
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="flex-center">
|
<div class="flex-center">
|
||||||
<FormTip content="-1不限制用户数量" />
|
<FormTip content="-1不限制用户数量" />
|
||||||
@ -270,7 +270,7 @@ watch(visible, () => {
|
|||||||
</template>
|
</template>
|
||||||
<NInputNumber v-model:value="model.accountCount" placeholder="请输入用户数量" min="-1" class="w-full" />
|
<NInputNumber v-model:value="model.accountCount" placeholder="请输入用户数量" min="-1" class="w-full" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
<NFormItem label="绑定域名" path="domain">
|
<NFormItem path="domain">
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="flex-center">
|
<div class="flex-center">
|
||||||
<FormTip
|
<FormTip
|
||||||
|
Loading…
Reference in New Issue
Block a user