chore: 重构组件

This commit is contained in:
xlsea 2025-04-27 17:06:19 +08:00
parent 19fe1b05eb
commit 9edd78e581
12 changed files with 123 additions and 42 deletions

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAttrs } from 'vue'; import { ref, useAttrs } from 'vue';
import type { UploadFileInfo, UploadProps } from 'naive-ui'; import type { UploadFileInfo, UploadProps } from 'naive-ui';
import { fetchBatchDeleteOss } from '@/service/api/system/oss'; import { fetchBatchDeleteOss } from '@/service/api/system/oss';
import { getToken } from '@/store/modules/auth/shared'; import { getToken } from '@/store/modules/auth/shared';
@ -29,6 +29,9 @@ const props = withDefaults(defineProps<Props>(), {
const attrs: UploadProps = useAttrs(); const attrs: UploadProps = useAttrs();
let fileNum = 0;
const fileList = ref<UploadFileInfo[]>([]);
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y'; const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy); const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
@ -38,6 +41,7 @@ const headers: Record<string, string> = {
}; };
function beforeUpload(options: { file: UploadFileInfo; fileList: UploadFileInfo[] }) { function beforeUpload(options: { file: UploadFileInfo; fileList: UploadFileInfo[] }) {
fileNum += 1;
const { file } = options; const { file } = options;
// //
@ -73,15 +77,19 @@ function isErrorState(xhr: XMLHttpRequest) {
} }
function handleFinish(options: { file: UploadFileInfo; event?: ProgressEvent }) { function handleFinish(options: { file: UploadFileInfo; event?: ProgressEvent }) {
fileNum -= 1;
const { file, event } = options; const { file, event } = options;
// @ts-expect-error Ignore type errors // @ts-expect-error Ignore type errors
const responseText = event?.target?.responseText; const responseText = event?.target?.responseText;
const response = JSON.parse(responseText); const response = JSON.parse(responseText);
const oss: Api.System.Oss = response.data; const oss: Api.System.Oss = response.data;
fileList.value.find(item => item.id === file.id)!.id = String(oss.ossId);
file.id = String(oss.ossId); file.id = String(oss.ossId);
file.url = oss.url; file.url = oss.url;
file.name = oss.fileName; file.name = oss.fileName;
window.$message?.success('上传成功'); if (fileNum === 0) {
window.$message?.success('上传成功');
}
return file; return file;
} }
@ -106,6 +114,7 @@ async function handleRemove(file: UploadFileInfo) {
<template> <template>
<NUpload <NUpload
v-bind="attrs" v-bind="attrs"
v-model:file-list="fileList"
:action="`${baseURL}${action}`" :action="`${baseURL}${action}`"
:headers="headers" :headers="headers"
:max="max" :max="max"

View File

@ -2,12 +2,20 @@ import { transformRecordToOption } from '@/utils/common';
/** enable status */ /** enable status */
export const enableStatusRecord: Record<Api.Common.EnableStatus, string> = { export const enableStatusRecord: Record<Api.Common.EnableStatus, string> = {
'0': '启用', '0': '正常',
'1': '用' '1': '用'
}; };
export const enableStatusOptions = transformRecordToOption(enableStatusRecord); export const enableStatusOptions = transformRecordToOption(enableStatusRecord);
/** yes or no status */
export const yesOrNoStatusRecord: Record<Api.Common.YesOrNoStatus, string> = {
Y: '是',
N: '否'
};
export const yesOrNoStatusOptions = transformRecordToOption(yesOrNoStatusRecord);
/** menu type */ /** menu type */
export const menuTypeRecord: Record<Api.System.MenuType, string> = { export const menuTypeRecord: Record<Api.System.MenuType, string> = {
M: '目录', M: '目录',
@ -91,3 +99,20 @@ export const genTplCategoryRecord: Record<Api.Tool.TplCategory, string> = {
}; };
export const genTplCategoryOptions = transformRecordToOption(genTplCategoryRecord); export const genTplCategoryOptions = transformRecordToOption(genTplCategoryRecord);
/** oss config is https */
export const ossConfigIsHttpsRecord: Record<Api.Common.YesOrNoStatus, string> = {
Y: 'https://',
N: 'http://'
};
export const ossConfigIsHttpsOptions = transformRecordToOption(ossConfigIsHttpsRecord);
/** oss access policy */
export const ossAccessPolicyRecord: Record<Api.System.OssAccessPolicy, string> = {
'0': '私有',
'1': '公有',
'2': '自定义'
};
export const ossAccessPolicyOptions = transformRecordToOption(ossAccessPolicyRecord);

View File

@ -48,7 +48,7 @@ watch(tenantId, async () => {
<GlobalBreadcrumb v-if="!appStore.isMobile" class="ml-12px" /> <GlobalBreadcrumb v-if="!appStore.isMobile" class="ml-12px" />
</div> </div>
<div class="h-full flex-y-center justify-end"> <div class="h-full flex-y-center justify-end">
<TenantSelect class="mr-12px w-150px" :clearable="true" /> <TenantSelect v-model:value="tenantId" class="mr-12px w-150px" />
<GlobalSearch /> <GlobalSearch />
<FullScreen v-if="!appStore.isMobile" :full="isFullscreen" @click="toggle" /> <FullScreen v-if="!appStore.isMobile" :full="isFullscreen" @click="toggle" />
<LangSwitch <LangSwitch

View File

@ -213,13 +213,14 @@ export const generatedRoutes: GeneratedRoute[] = [
}, },
{ {
name: 'system_oss-config', name: 'system_oss-config',
path: '/oss-config', path: '/system/oss-config',
component: 'view.system_oss-config', component: 'view.system_oss-config',
meta: { meta: {
title: 'system_oss-config', title: 'system_oss-config',
i18nKey: 'route.system_oss-config', i18nKey: 'route.system_oss-config',
constant: true, constant: true,
hideInMenu: true hideInMenu: true,
icon: 'hugeicons:configuration-01'
} }
}, },
{ {

View File

@ -183,7 +183,7 @@ const routeMap: RouteMap = {
"system_menu": "/system/menu", "system_menu": "/system/menu",
"system_notice": "/system/notice", "system_notice": "/system/notice",
"system_oss": "/system/oss", "system_oss": "/system/oss",
"system_oss-config": "/oss-config", "system_oss-config": "/system/oss-config",
"system_post": "/system/post", "system_post": "/system/post",
"system_tenant": "/system/tenant", "system_tenant": "/system/tenant",
"system_user": "/system/user", "system_user": "/system/user",

View File

@ -126,7 +126,7 @@ const dynamicConstantRoutes: ElegantRoute[] = [
children: [ children: [
{ {
name: 'system_oss-config', name: 'system_oss-config',
path: '/oss-config', path: '/system/oss-config',
component: 'view.system_oss-config', component: 'view.system_oss-config',
meta: { meta: {
title: 'system_oss-config', title: 'system_oss-config',

View File

@ -47,10 +47,10 @@ declare namespace Api {
/** /**
* *
* *
* - "0": * - "Y":
* - "1": * - "N":
*/ */
type YesOrNoStatus = '0' | '1'; type YesOrNoStatus = 'Y' | 'N';
/** common record */ /** common record */
type CommonRecord<T = any> = { type CommonRecord<T = any> = {

View File

@ -182,7 +182,7 @@ declare namespace Api {
/** 是否为外链0是 1否 2iframe */ /** 是否为外链0是 1否 2iframe */
isFrame: IsMenuFrame; isFrame: IsMenuFrame;
/** 是否缓存0缓存 1不缓存 */ /** 是否缓存0缓存 1不缓存 */
isCache: Common.YesOrNoStatus; isCache: Common.EnableStatus;
/** 菜单类型M目录 C菜单 F按钮 */ /** 菜单类型M目录 C菜单 F按钮 */
menuType: MenuType; menuType: MenuType;
/** 显示状态0显示 1隐藏 */ /** 显示状态0显示 1隐藏 */
@ -643,6 +643,9 @@ declare namespace Api {
/** oss list */ /** oss list */
type OssList = Api.Common.PaginatingQueryRecord<Oss>; type OssList = Api.Common.PaginatingQueryRecord<Oss>;
/** oss access policy */
type OssAccessPolicy = '0' | '1' | '2';
/** oss config */ /** oss config */
type OssConfig = Common.CommonRecord<{ type OssConfig = Common.CommonRecord<{
/** 主键 */ /** 主键 */
@ -664,13 +667,13 @@ declare namespace Api {
/** 自定义域名 */ /** 自定义域名 */
domain: string; domain: string;
/** 是否httpsY=是,N=否) */ /** 是否httpsY=是,N=否) */
isHttps: string; isHttps: Api.Common.YesOrNoStatus;
/** 域 */ /** 域 */
region: string; region: string;
/** 桶权限类型 */ /** 桶权限类型 */
accessPolicy: string; accessPolicy: Api.System.OssAccessPolicy;
/** 是否默认0=是,1=否) */ /** 是否默认0=是,1=否) */
status: string; status: Api.Common.EnableStatus;
/** 扩展字段 */ /** 扩展字段 */
ext1: string; ext1: string;
/** 备注 */ /** 备注 */

View File

@ -37,7 +37,7 @@ declare module "@elegant-router/types" {
"system_menu": "/system/menu"; "system_menu": "/system/menu";
"system_notice": "/system/notice"; "system_notice": "/system/notice";
"system_oss": "/system/oss"; "system_oss": "/system/oss";
"system_oss-config": "/oss-config"; "system_oss-config": "/system/oss-config";
"system_post": "/system/post"; "system_post": "/system/post";
"system_tenant": "/system/tenant"; "system_tenant": "/system/tenant";
"system_user": "/system/user"; "system_user": "/system/user";
@ -117,8 +117,8 @@ declare module "@elegant-router/types" {
| "system_dict_type" | "system_dict_type"
| "system_menu" | "system_menu"
| "system_notice" | "system_notice"
| "system_oss"
| "system_oss-config" | "system_oss-config"
| "system_oss"
| "system_post" | "system_post"
| "system_tenant" | "system_tenant"
| "system_user" | "system_user"

26
src/utils/copy.ts Normal file
View File

@ -0,0 +1,26 @@
import { useClipboard } from '@vueuse/core';
const { copy, isSupported } = useClipboard();
export async function handleCopy(source?: string) {
if (!isSupported) {
window.$message?.error('您的浏览器不支持 Clipboard API');
return;
}
if (!source) {
return;
}
if (navigator.clipboard && window.isSecureContext) {
await copy(source);
} else {
const range = document.createRange();
range.selectNode(document.getElementById('tokenDetailInput')!);
const selection = window.getSelection();
if (selection?.rangeCount) selection.removeAllRanges();
selection?.addRange(range);
document.execCommand('copy');
}
window.$message?.success('复制成功');
}

View File

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue'; import { computed, reactive, watch } from 'vue';
import { ossAccessPolicyOptions, ossConfigIsHttpsOptions } from '@/constants/business';
import { fetchCreateOssConfig, fetchUpdateOssConfig } from '@/service/api/system/oss-config'; import { fetchCreateOssConfig, fetchUpdateOssConfig } from '@/service/api/system/oss-config';
import { useFormRules, useNaiveForm } from '@/hooks/common/form'; import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales'; import { $t } from '@/locales';
@ -51,9 +52,9 @@ function createDefaultModel(): Model {
prefix: '', prefix: '',
endpoint: '', endpoint: '',
domain: '', domain: '',
isHttps: '0', isHttps: 'N',
region: '', region: '',
accessPolicy: '0', accessPolicy: '1',
remark: '' remark: ''
}; };
} }
@ -73,12 +74,6 @@ const rules: Record<RuleKey, App.Global.FormRule> = {
accessPolicy: createRequiredRule('桶权限类型不能为空') accessPolicy: createRequiredRule('桶权限类型不能为空')
}; };
const accessPolicyOptions = ref<CommonType.Option[]>([
{ label: '私有', value: '0' },
{ label: '公有', value: '1' },
{ label: '自定义', value: '2' }
]);
function handleUpdateModelWhenEdit() { function handleUpdateModelWhenEdit() {
if (props.operateType === 'add') { if (props.operateType === 'add') {
Object.assign(model, createDefaultModel()); Object.assign(model, createDefaultModel());
@ -183,7 +178,12 @@ watch(visible, () => {
</NFormItem> </NFormItem>
<NFormItem label="访问站点" path="endpoint"> <NFormItem label="访问站点" path="endpoint">
<NInputGroup> <NInputGroup>
<NInputGroupLabel>http://</NInputGroupLabel> <NSelect
v-model:value="model.isHttps"
class="w-110px"
:options="ossConfigIsHttpsOptions"
placeholder="请选择访问协议"
/>
<NInput v-model:value="model.endpoint" placeholder="请输入访问站点" /> <NInput v-model:value="model.endpoint" placeholder="请输入访问站点" />
</NInputGroup> </NInputGroup>
</NFormItem> </NFormItem>
@ -192,10 +192,10 @@ watch(visible, () => {
</NFormItem> </NFormItem>
<NDivider>认证信息</NDivider> <NDivider>认证信息</NDivider>
<NFormItem label="accessKey" path="accessKey"> <NFormItem label="accessKey" path="accessKey">
<NInput v-model:value="model.accessKey" placeholder="请输入accessKey" /> <NInput v-model:value="model.accessKey" placeholder="请输入 AccessKey" />
</NFormItem> </NFormItem>
<NFormItem label="secretKey" path="secretKey"> <NFormItem label="secretKey" path="secretKey">
<NInput v-model:value="model.secretKey" placeholder="请输入秘钥secretKey" /> <NInput v-model:value="model.secretKey" placeholder="请输入秘钥 SecretKey" />
</NFormItem> </NFormItem>
<NDivider>桶信息</NDivider> <NDivider>桶信息</NDivider>
<NFormItem label="桶名称" path="bucketName"> <NFormItem label="桶名称" path="bucketName">
@ -205,17 +205,12 @@ watch(visible, () => {
<NInput v-model:value="model.prefix" placeholder="请输入前缀" /> <NInput v-model:value="model.prefix" placeholder="请输入前缀" />
</NFormItem> </NFormItem>
<NGrid :cols="2" :x-gap="24"> <NGrid :cols="2" :x-gap="24">
<NGridItem>
<NFormItem label="是否https" path="isHttps">
<DictRadio v-model:value="model.isHttps" dict-code="sys_yes_no" />
</NFormItem>
</NGridItem>
<NGridItem> <NGridItem>
<NFormItem label="桶权限类型" path="accessPolicy"> <NFormItem label="桶权限类型" path="accessPolicy">
<NRadioGroup v-model:value="model.accessPolicy"> <NRadioGroup v-model:value="model.accessPolicy">
<NSpace> <NSpace>
<NRadio <NRadio
v-for="option in accessPolicyOptions" v-for="option in ossAccessPolicyOptions"
:key="option.value" :key="option.value"
:value="option.value" :value="option.value"
:label="option.label" :label="option.label"

View File

@ -1,6 +1,6 @@
<script setup lang="tsx"> <script setup lang="tsx">
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { NImage, NTag } from 'naive-ui'; import { NButton, NEllipsis, NImage, NTag, NTooltip } from 'naive-ui';
import { useBoolean, useLoading } from '@sa/hooks'; import { useBoolean, useLoading } from '@sa/hooks';
import { fetchBatchDeleteOss, fetchGetOssList } from '@/service/api/system/oss'; import { fetchBatchDeleteOss, fetchGetOssList } from '@/service/api/system/oss';
import { fetchGetConfigByKey, fetchUpdateConfigByKey } from '@/service/api/system/config'; import { fetchGetConfigByKey, fetchUpdateConfigByKey } from '@/service/api/system/config';
@ -10,6 +10,7 @@ import { useAuth } from '@/hooks/business/auth';
import { useDownload } from '@/hooks/business/download'; import { useDownload } from '@/hooks/business/download';
import { useRouterPush } from '@/hooks/common/router'; import { useRouterPush } from '@/hooks/common/router';
import { isImage } from '@/utils/common'; import { isImage } from '@/utils/common';
import { handleCopy } from '@/utils/copy';
import { $t } from '@/locales'; import { $t } from '@/locales';
import ButtonIcon from '@/components/custom/button-icon.vue'; import ButtonIcon from '@/components/custom/button-icon.vue';
import OssSearch from './modules/oss-search.vue'; import OssSearch from './modules/oss-search.vue';
@ -71,19 +72,27 @@ const {
key: 'fileName', key: 'fileName',
title: '文件名', title: '文件名',
align: 'center', align: 'center',
ellipsis: {
tooltip: true,
lineClamp: 3
},
minWidth: 120 minWidth: 120
}, },
{ {
key: 'originalName', key: 'originalName',
title: '原名', title: '原名',
align: 'center', align: 'center',
ellipsis: {
tooltip: true,
lineClamp: 3
},
minWidth: 120 minWidth: 120
}, },
{ {
key: 'fileSuffix', key: 'fileSuffix',
title: '文件后缀名', title: '文件后缀名',
align: 'center', align: 'center',
minWidth: 120 minWidth: 100
}, },
{ {
key: 'url', key: 'url',
@ -94,7 +103,20 @@ const {
if (preview.value && isImage(row.fileSuffix)) { if (preview.value && isImage(row.fileSuffix)) {
return <NImage class="h-40px w-40px object-contain" src={row.url} />; return <NImage class="h-40px w-40px object-contain" src={row.url} />;
} }
return <span>{row.url}</span>; return (
<NTooltip>
{{
default: () => <span>点击复制</span>,
trigger: () => (
<div class="cursor-pointer" onClick={async () => await handleCopy(row.url)}>
<NEllipsis line-clamp={3} tooltip={false}>
{row.url}
</NEllipsis>
</div>
)
}}
</NTooltip>
);
} }
}, },
{ {
@ -113,7 +135,7 @@ const {
key: 'service', key: 'service',
title: '服务商', title: '服务商',
align: 'center', align: 'center',
minWidth: 120, minWidth: 100,
render: row => { render: row => {
return <NTag type="primary">{row.service}</NTag>; return <NTag type="primary">{row.service}</NTag>;
} }