@liujitcn/kratos-admin-core 0.0.31 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -4
- package/dist/package/declarations/src/api/base/{config.d.ts → v1/config.d.ts} +1 -1
- package/dist/package/declarations/src/api/base/{file.d.ts → v1/file.d.ts} +3 -1
- package/dist/package/declarations/src/api/base/{language.d.ts → v1/language.d.ts} +1 -1
- package/dist/package/declarations/src/api/base/{login.d.ts → v1/login.d.ts} +2 -2
- package/dist/package/declarations/src/api/base/{mfa.d.ts → v1/mfa.d.ts} +3 -3
- package/dist/package/declarations/src/api/base/{oauth.d.ts → v1/oauth.d.ts} +1 -1
- package/dist/package/declarations/src/api/system/{auth.d.ts → admin/v1/auth.d.ts} +2 -2
- package/dist/package/declarations/src/api/system/{base_dict.d.ts → admin/v1/base_dict.d.ts} +1 -1
- package/dist/package/declarations/src/api/system/{base_tenant.d.ts → admin/v1/base_tenant.d.ts} +2 -2
- package/dist/package/declarations/src/components/ProTable/index.vue.d.ts +114 -0
- package/dist/package/declarations/src/components/TreeFilter/index.vue.d.ts +84 -76
- package/dist/package/declarations/src/components/Upload/File.vue.d.ts +1 -1
- package/dist/package/declarations/src/components/Upload/Files.vue.d.ts +1 -1
- package/dist/package/declarations/src/components/Upload/Img.vue.d.ts +1 -1
- package/dist/package/declarations/src/components/Upload/Imgs.vue.d.ts +1 -1
- package/dist/package/declarations/src/components/WangEditor/index.vue.d.ts +2 -0
- package/dist/package/declarations/src/documentTitle.d.ts +2 -0
- package/dist/package/declarations/src/format.d.ts +1 -1
- package/dist/package/declarations/src/index.d.ts +2 -1
- package/dist/package/declarations/src/locales/generated.d.ts +68 -0
- package/dist/package/declarations/src/modules/index.d.ts +1 -3
- package/dist/package/declarations/src/request.d.ts +1 -1
- package/dist/package/declarations/src/routers/index.d.ts +1 -1
- package/dist/package/declarations/src/rpc/base/v1/login.d.ts +5 -3
- package/dist/package/declarations/src/rpc/system/admin/v1/auth.d.ts +13 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +0 -101
- package/dist/package/declarations/src/rpc/system/common/v1/common.d.ts +26 -0
- package/dist/package/declarations/src/stores/interface/index.d.ts +6 -1
- package/dist/package/declarations/src/stores/modules/user.d.ts +2 -4
- package/dist/package/declarations/src/utils/passwordStrength.d.ts +1 -12
- package/dist/package/declarations/src/utils/request.d.ts +13 -2
- package/dist/package/declarations/src/utils/utils.d.ts +4 -0
- package/dist/package/src/api/base/{config.ts → v1/config.ts} +2 -2
- package/dist/package/src/api/base/{file.ts → v1/file.ts} +20 -24
- package/dist/package/src/api/base/{language.ts → v1/language.ts} +2 -2
- package/dist/package/src/api/base/{login.ts → v1/login.ts} +3 -3
- package/dist/package/src/api/base/{mfa.ts → v1/mfa.ts} +4 -4
- package/dist/package/src/api/base/{oauth.ts → v1/oauth.ts} +2 -2
- package/dist/package/src/api/system/{auth.ts → admin/v1/auth.ts} +3 -3
- package/dist/package/src/api/system/{base_dict.ts → admin/v1/base_dict.ts} +2 -2
- package/dist/package/src/api/system/{base_tenant.ts → admin/v1/base_tenant.ts} +3 -3
- package/dist/package/src/bootstrap.ts +6 -5
- package/dist/package/src/components/CronExpression/index.vue +4 -3
- package/dist/package/src/components/Dialog/ProDialog.vue +1 -0
- package/dist/package/src/components/ImportExcel/index.vue +4 -2
- package/dist/package/src/components/MarkdownPreview/index.vue +1 -1
- package/dist/package/src/components/PasswordStrength/index.vue +1 -1
- package/dist/package/src/components/ProTable/components/TableColumn.vue +14 -6
- package/dist/package/src/components/ProTable/index.vue +3 -3
- package/dist/package/src/components/RichTextPreview/index.vue +12 -1
- package/dist/package/src/components/SelectIcon/index.vue +3 -2
- package/dist/package/src/components/Upload/File.vue +2 -2
- package/dist/package/src/components/Upload/Files.vue +2 -2
- package/dist/package/src/components/Upload/Img.vue +7 -5
- package/dist/package/src/components/Upload/Imgs.vue +6 -5
- package/dist/package/src/components/WangEditor/index.vue +10 -7
- package/dist/package/src/documentTitle.ts +8 -0
- package/dist/package/src/format.ts +1 -1
- package/dist/package/src/index.ts +2 -1
- package/dist/package/src/layouts/components/Header/components/Avatar.vue +7 -6
- package/dist/package/src/layouts/components/Header/components/InfoDialog.vue +3 -2
- package/dist/package/src/layouts/components/Header/components/PasswordDialog.vue +3 -2
- package/dist/package/src/layouts/components/Header/components/SearchMenu.vue +16 -8
- package/dist/package/src/layouts/components/LockScreen.vue +175 -52
- package/dist/package/src/layouts/components/Tabs/index.vue +12 -3
- package/dist/package/src/layouts/components/ThemeDrawer/index.scss +4 -4
- package/dist/package/src/locales/en-US.json +15 -2
- package/dist/package/src/locales/ja-JP.json +15 -2
- package/dist/package/src/locales/zh-CN.json +15 -2
- package/dist/package/src/locales/zh-TW.json +15 -2
- package/dist/package/src/modules/index.ts +2 -4
- package/dist/package/src/request.ts +3 -0
- package/dist/package/src/routers/index.ts +15 -14
- package/dist/package/src/rpc/base/v1/config.ts +1 -1
- package/dist/package/src/rpc/base/v1/file.ts +1 -1
- package/dist/package/src/rpc/base/v1/language.ts +1 -1
- package/dist/package/src/rpc/base/v1/login.ts +5 -3
- package/dist/package/src/rpc/base/v1/mfa.ts +1 -1
- package/dist/package/src/rpc/base/v1/oauth.ts +1 -1
- package/dist/package/src/rpc/base/v1/oauth_client.ts +1 -1
- package/dist/package/src/rpc/common/v1/common.ts +1 -1
- package/dist/package/src/rpc/common/v1/enum.ts +1 -1
- package/dist/package/src/rpc/common/v1/types.ts +1 -1
- package/dist/package/src/rpc/google/protobuf/empty.ts +1 -1
- package/dist/package/src/rpc/google/protobuf/wrappers.ts +1 -1
- package/dist/package/src/rpc/system/admin/v1/auth.ts +14 -1
- package/dist/package/src/rpc/system/admin/v1/base_dict.ts +1 -113
- package/dist/package/src/rpc/system/admin/v1/base_i18n.ts +1 -1
- package/dist/package/src/rpc/system/admin/v1/base_tenant.ts +1 -1
- package/dist/package/src/rpc/system/admin/v1/common.ts +1 -1
- package/dist/package/src/rpc/system/common/v1/common.ts +35 -0
- package/dist/package/src/stores/interface/index.ts +6 -1
- package/dist/package/src/stores/modules/auth.ts +1 -1
- package/dist/package/src/stores/modules/config.ts +2 -2
- package/dist/package/src/stores/modules/dict.ts +1 -1
- package/dist/package/src/stores/modules/user.ts +21 -22
- package/dist/package/src/styles/element.scss +35 -4
- package/dist/package/src/typings/global.d.ts +6 -0
- package/dist/package/src/utils/passwordCrypto.ts +169 -12
- package/dist/package/src/utils/passwordStrength.ts +7 -24
- package/dist/package/src/utils/request.ts +155 -36
- package/dist/package/src/utils/tenant.ts +1 -1
- package/dist/package/src/utils/utils.ts +25 -6
- package/dist/package/src/views/login/components/LoginForm.vue +36 -21
- package/dist/package/src/views/login/index.scss +2 -2
- package/dist/package/types/generated/auto-imports.d.ts +2 -0
- package/package.json +2 -1
- package/types/generated/auto-imports.d.ts +2 -0
- package/vite.config.ts +24 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import service from "
|
|
2
|
-
import type { DownloadFileRequest, MultiUploadFileResponse, FileInfo } from "
|
|
1
|
+
import service from "../../../utils/request";
|
|
2
|
+
import type { DownloadFileRequest, MultiUploadFileResponse, FileInfo } from "../../../rpc/base/v1/file";
|
|
3
3
|
|
|
4
4
|
const FILE_URL = "/v1/base/file";
|
|
5
5
|
|
|
@@ -35,33 +35,29 @@ export class FileServiceImpl {
|
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
/** 读取文件内容并返回 Blob。 */
|
|
40
|
+
async GetFileBlob(file: string, fileName: string): Promise<Blob> {
|
|
41
|
+
const response = await service<DownloadFileRequest, any>({
|
|
42
|
+
url: `${FILE_URL}`,
|
|
43
|
+
method: "get",
|
|
44
|
+
params: {
|
|
45
|
+
name: fileName,
|
|
46
|
+
path: file
|
|
47
|
+
} as DownloadFileRequest,
|
|
48
|
+
responseType: "blob"
|
|
49
|
+
});
|
|
50
|
+
return response.data instanceof Blob ? response.data : new Blob([response.data]);
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
/** 下载文件 */
|
|
39
|
-
async DownloadFile(file: string, fileName: string) {
|
|
54
|
+
async DownloadFile(file: string, fileName: string): Promise<void> {
|
|
40
55
|
try {
|
|
41
|
-
const response = await service<DownloadFileRequest, any>({
|
|
42
|
-
url: `${FILE_URL}`,
|
|
43
|
-
method: "get",
|
|
44
|
-
params: {
|
|
45
|
-
name: fileName,
|
|
46
|
-
path: file
|
|
47
|
-
} as DownloadFileRequest,
|
|
48
|
-
responseType: "blob" // 明确要求返回 Blob 类型
|
|
49
|
-
});
|
|
50
|
-
// 获取文件名
|
|
51
|
-
const contentDisposition = response.headers["content-disposition"];
|
|
52
|
-
let downloadFilename = fileName;
|
|
53
|
-
if (contentDisposition) {
|
|
54
|
-
const match = contentDisposition.match(/filename="?(.+?)"?$/);
|
|
55
|
-
if (match && match[1]) {
|
|
56
|
-
downloadFilename = match[1];
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
56
|
// 创建下载链接
|
|
61
|
-
const url = window.URL.createObjectURL(
|
|
57
|
+
const url = window.URL.createObjectURL(await this.GetFileBlob(file, fileName));
|
|
62
58
|
const a = document.createElement("a");
|
|
63
59
|
a.href = url;
|
|
64
|
-
a.download =
|
|
60
|
+
a.download = fileName;
|
|
65
61
|
document.body.appendChild(a);
|
|
66
62
|
a.click();
|
|
67
63
|
document.body.removeChild(a);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import service from "
|
|
2
|
-
import type { LanguageService, OptionLanguageRequest, OptionLanguageResponse } from "
|
|
1
|
+
import service from "../../../utils/request";
|
|
2
|
+
import type { LanguageService, OptionLanguageRequest, OptionLanguageResponse } from "../../../rpc/base/v1/language";
|
|
3
3
|
|
|
4
4
|
const LANGUAGE_URL = "/v1/base/language";
|
|
5
5
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import service from "
|
|
1
|
+
import service from "../../../utils/request";
|
|
2
2
|
import type {
|
|
3
3
|
CaptchaRequest,
|
|
4
4
|
CaptchaResponse,
|
|
@@ -12,8 +12,8 @@ import type {
|
|
|
12
12
|
VerifyCaptchaRequest,
|
|
13
13
|
VerifyCaptchaResponse,
|
|
14
14
|
LoginService
|
|
15
|
-
} from "
|
|
16
|
-
import type { Empty } from "
|
|
15
|
+
} from "../../../rpc/base/v1/login";
|
|
16
|
+
import type { Empty } from "../../../rpc/google/protobuf/empty";
|
|
17
17
|
|
|
18
18
|
const CAPTCHA_URL = "/v1/base/captcha";
|
|
19
19
|
const CAPTCHA_VERIFY_URL = "/v1/base/captcha/verify";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import service from "
|
|
2
|
-
import type { LoginResponse } from "
|
|
1
|
+
import service from "../../../utils/request";
|
|
2
|
+
import type { LoginResponse } from "../../../rpc/base/v1/login";
|
|
3
3
|
import type {
|
|
4
4
|
BeginMfaDisableRequest,
|
|
5
5
|
BeginMfaDisableResponse,
|
|
@@ -18,8 +18,8 @@ import type {
|
|
|
18
18
|
RecoveryCodesResponse,
|
|
19
19
|
RegenerateMfaRecoveryCodesRequest,
|
|
20
20
|
VerifyMfaRequest
|
|
21
|
-
} from "
|
|
22
|
-
import type { Empty } from "
|
|
21
|
+
} from "../../../rpc/base/v1/mfa";
|
|
22
|
+
import type { Empty } from "../../../rpc/google/protobuf/empty";
|
|
23
23
|
|
|
24
24
|
const MFA_URL = "/v1/base/mfa";
|
|
25
25
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import service from "
|
|
1
|
+
import service from "../../../utils/request";
|
|
2
2
|
import type {
|
|
3
3
|
CreateOauthAuthorizationRequest,
|
|
4
4
|
CreateOauthAuthorizationResponse,
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
ExchangeOauthTicketResponse,
|
|
7
7
|
ListOauthProviderRequest,
|
|
8
8
|
ListOauthProviderResponse
|
|
9
|
-
} from "
|
|
9
|
+
} from "../../../rpc/base/v1/oauth";
|
|
10
10
|
|
|
11
11
|
const OAUTH_PROVIDER_URL = "/v1/base/oauth/provider";
|
|
12
12
|
const OAUTH_AUTHORIZATION_URL = "/v1/base/oauth/authorization";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import service from "
|
|
1
|
+
import service from "../../../../utils/request";
|
|
2
2
|
import type {
|
|
3
3
|
GetUserInfoRequest,
|
|
4
4
|
ListUserButtonRequest,
|
|
5
5
|
TreeRouteResponse,
|
|
6
6
|
TreeUserMenuRequest,
|
|
7
7
|
UserInfoForm
|
|
8
|
-
} from "
|
|
9
|
-
import type { StringValues } from "
|
|
8
|
+
} from "../../../../rpc/system/admin/v1/auth";
|
|
9
|
+
import type { StringValues } from "../../../../rpc/common/v1/types";
|
|
10
10
|
|
|
11
11
|
const AUTH_URL = "/v1/admin/auth";
|
|
12
12
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import service from "
|
|
2
|
-
import type { OptionBaseDictRequest, OptionBaseDictResponse } from "
|
|
1
|
+
import service from "../../../../utils/request";
|
|
2
|
+
import type { OptionBaseDictRequest, OptionBaseDictResponse } from "../../../../rpc/system/admin/v1/base_dict";
|
|
3
3
|
|
|
4
4
|
const BASE_DICT_URL = "/v1/admin/base/dict";
|
|
5
5
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import service from "
|
|
2
|
-
import type { SelectOptionResponse } from "
|
|
3
|
-
import type { OptionBaseTenantRequest } from "
|
|
1
|
+
import service from "../../../../utils/request";
|
|
2
|
+
import type { SelectOptionResponse } from "../../../../rpc/common/v1/common";
|
|
3
|
+
import type { OptionBaseTenantRequest } from "../../../../rpc/system/admin/v1/base_tenant";
|
|
4
4
|
|
|
5
5
|
const BASE_TENANT_URL = "/v1/admin/base/tenant";
|
|
6
6
|
|
|
@@ -28,7 +28,8 @@ import { useDictStore } from "./stores/modules/dict";
|
|
|
28
28
|
import { useTabsStore } from "./stores/modules/tabs";
|
|
29
29
|
import { useUserStore } from "./stores/modules/user";
|
|
30
30
|
import { getRouteMetaTitle } from "./utils";
|
|
31
|
-
import { defLanguageService } from "./api/base/language";
|
|
31
|
+
import { defLanguageService } from "./api/base/v1/language";
|
|
32
|
+
import { setAdminDocumentTitle } from "./documentTitle";
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* 管理端启动参数。
|
|
@@ -44,7 +45,7 @@ export interface AdminBootstrapOptions {
|
|
|
44
45
|
* 创建并启动管理端应用,业务项目可通过 modules 注册自己的页面。
|
|
45
46
|
*/
|
|
46
47
|
export async function bootstrapAdminApp(options: AdminBootstrapOptions = {}) {
|
|
47
|
-
|
|
48
|
+
const modules = [kratosAdminModule, ...(options.modules ?? [])];
|
|
48
49
|
registerAdminModules(modules);
|
|
49
50
|
registerLocaleMessages(modules);
|
|
50
51
|
initializeLocale();
|
|
@@ -79,6 +80,7 @@ export async function bootstrapAdminApp(options: AdminBootstrapOptions = {}) {
|
|
|
79
80
|
} catch {
|
|
80
81
|
// 配置接口失败时继续使用本地默认配置,避免阻断应用启动。
|
|
81
82
|
}
|
|
83
|
+
updateDocumentTitle();
|
|
82
84
|
|
|
83
85
|
app.mount(options.mount ?? "#app");
|
|
84
86
|
return app;
|
|
@@ -91,7 +93,7 @@ async function refreshLocalizedRuntimeData() {
|
|
|
91
93
|
const dictStore = useDictStore(pinia);
|
|
92
94
|
const configStore = useConfigStore(pinia);
|
|
93
95
|
await Promise.allSettled([configStore.loadDisplayConfig()]);
|
|
94
|
-
if (userStore.token
|
|
96
|
+
if (userStore.token) {
|
|
95
97
|
await Promise.allSettled([authStore.getAuthMenuList(), dictStore.updateDictionaryCache()]);
|
|
96
98
|
syncLocalizedRouteState();
|
|
97
99
|
}
|
|
@@ -119,6 +121,5 @@ function updateDocumentTitle() {
|
|
|
119
121
|
const route = router.currentRoute.value;
|
|
120
122
|
const titleKey = typeof route.meta.titleKey === "string" ? route.meta.titleKey : "";
|
|
121
123
|
const routeTitle = titleKey ? t(titleKey) : String(route.meta.title ?? "");
|
|
122
|
-
|
|
123
|
-
document.title = routeTitle ? `${routeTitle} - ${appTitle}` : appTitle;
|
|
124
|
+
setAdminDocumentTitle(routeTitle);
|
|
124
125
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
</template>
|
|
13
13
|
</el-input>
|
|
14
14
|
|
|
15
|
-
<
|
|
15
|
+
<ProDialog v-model="dialogVisible" :title="t('core.cron.title')" width="980px" top="2vh" destroy-on-close>
|
|
16
16
|
<div class="cron-editor">
|
|
17
17
|
<div class="cron-editor__preset">
|
|
18
18
|
<div class="cron-editor__section-title">{{ t("core.cron.presets") }}</div>
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
<el-button @click="dialogVisible = false">{{ t("common.action.cancel") }}</el-button>
|
|
108
108
|
</div>
|
|
109
109
|
</template>
|
|
110
|
-
</
|
|
110
|
+
</ProDialog>
|
|
111
111
|
</div>
|
|
112
112
|
</template>
|
|
113
113
|
|
|
@@ -116,6 +116,7 @@ import { computed, defineComponent, h, reactive, ref, watch } from "vue";
|
|
|
116
116
|
import type { PropType } from "vue";
|
|
117
117
|
import { ElCheckbox, ElCheckboxGroup, ElInputNumber, ElRadio } from "element-plus";
|
|
118
118
|
import { Operation } from "@element-plus/icons-vue";
|
|
119
|
+
import ProDialog from "../../components/Dialog/ProDialog.vue";
|
|
119
120
|
import { useLocaleStore } from "../../locales";
|
|
120
121
|
|
|
121
122
|
const { t } = useLocaleStore();
|
|
@@ -669,7 +670,7 @@ const CronSegmentEditor = defineComponent({
|
|
|
669
670
|
gap: 8px;
|
|
670
671
|
padding: 12px;
|
|
671
672
|
background: var(--el-fill-color-light);
|
|
672
|
-
border-radius:
|
|
673
|
+
border-radius: var(--admin-page-radius);
|
|
673
674
|
}
|
|
674
675
|
.cron-editor__preset-list {
|
|
675
676
|
display: flex;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<ProDialog
|
|
3
3
|
v-model="dialogVisible"
|
|
4
4
|
:title="t('core.upload.batch_add', { resource: parameter.title })"
|
|
5
5
|
:destroy-on-close="true"
|
|
6
6
|
width="580px"
|
|
7
|
+
:show-footer="false"
|
|
7
8
|
draggable
|
|
8
9
|
>
|
|
9
10
|
<el-form class="drawer-multiColumn-form" label-width="180px">
|
|
@@ -44,11 +45,12 @@
|
|
|
44
45
|
<el-switch v-model="isCover" />
|
|
45
46
|
</el-form-item>
|
|
46
47
|
</el-form>
|
|
47
|
-
</
|
|
48
|
+
</ProDialog>
|
|
48
49
|
</template>
|
|
49
50
|
|
|
50
51
|
<script setup lang="ts" name="ImportExcel">
|
|
51
52
|
import { ref } from "vue";
|
|
53
|
+
import ProDialog from "../../components/Dialog/ProDialog.vue";
|
|
52
54
|
import { useDownload } from "../../hooks/useDownload";
|
|
53
55
|
import { Download } from "@element-plus/icons-vue";
|
|
54
56
|
import { ElNotification, UploadRequestOptions, UploadRawFile } from "element-plus";
|
|
@@ -241,7 +241,7 @@ function createMarkdownHeadingId({ text, index }: Parameters<MdHeadingId>[0]) {
|
|
|
241
241
|
cursor: pointer;
|
|
242
242
|
background: var(--admin-page-card-bg);
|
|
243
243
|
border: 1px solid var(--admin-page-card-border-soft);
|
|
244
|
-
border-radius:
|
|
244
|
+
border-radius: var(--admin-page-radius);
|
|
245
245
|
box-shadow: var(--admin-page-shadow);
|
|
246
246
|
}
|
|
247
247
|
.core-markdown-preview :deep(.md-editor-code pre) {
|
|
@@ -53,7 +53,7 @@ const strength = computed(() => getPasswordStrength(props.password));
|
|
|
53
53
|
padding: 14px 16px;
|
|
54
54
|
background: #fafbfd;
|
|
55
55
|
border: 1px solid #ebeef5;
|
|
56
|
-
border-radius:
|
|
56
|
+
border-radius: var(--admin-page-radius);
|
|
57
57
|
}
|
|
58
58
|
.password-strength__header {
|
|
59
59
|
display: flex;
|
|
@@ -9,7 +9,7 @@ import DictLabel from "../../../components/Dict/DictLabel.vue";
|
|
|
9
9
|
import { ColumnProps, HeaderRenderScope, RenderScope, TableActionProps } from "../../../components/ProTable/interface";
|
|
10
10
|
import type { TableAlign } from "../../../utils/proTable";
|
|
11
11
|
import { filterEnum, formatValue, handleProp, handleRowAccordingToProp } from "../../../utils";
|
|
12
|
-
import { formatPrice } from "../../../utils/utils";
|
|
12
|
+
import { formatPrice, formatSrc } from "../../../utils/utils";
|
|
13
13
|
import { t } from "../../../locales";
|
|
14
14
|
|
|
15
15
|
const props = defineProps<{
|
|
@@ -93,12 +93,14 @@ const getBooleanValue = (value: boolean | ((scope: RenderScope<any>) => boolean)
|
|
|
93
93
|
*/
|
|
94
94
|
const renderImageCell = (item: ColumnProps, scope: RenderScope<any>) => {
|
|
95
95
|
const imageProps = item.imageProps ?? {};
|
|
96
|
-
const
|
|
96
|
+
const rawSrc =
|
|
97
97
|
typeof imageProps.src === "function"
|
|
98
98
|
? imageProps.src(scope)
|
|
99
99
|
: (imageProps.src ?? handleRowAccordingToProp(scope.row, item.prop!));
|
|
100
|
-
const
|
|
101
|
-
if (!
|
|
100
|
+
const rawPreviewSrc = typeof imageProps.previewSrc === "function" ? imageProps.previewSrc(scope) : (imageProps.previewSrc ?? rawSrc);
|
|
101
|
+
if (!rawSrc || rawSrc === "--") return "--";
|
|
102
|
+
const src = formatSrc(String(rawSrc));
|
|
103
|
+
const previewSrc = formatSrc(String(rawPreviewSrc));
|
|
102
104
|
const thumbWidth = typeof imageProps.width === "number" ? `${imageProps.width}px` : (imageProps.width ?? "60px");
|
|
103
105
|
const thumbHeight = typeof imageProps.height === "number" ? `${imageProps.height}px` : (imageProps.height ?? "60px");
|
|
104
106
|
return h(
|
|
@@ -116,7 +118,7 @@ const renderImageCell = (item: ColumnProps, scope: RenderScope<any>) => {
|
|
|
116
118
|
style: {
|
|
117
119
|
width: thumbWidth,
|
|
118
120
|
height: thumbHeight,
|
|
119
|
-
borderRadius: "
|
|
121
|
+
borderRadius: "var(--admin-page-radius)"
|
|
120
122
|
}
|
|
121
123
|
},
|
|
122
124
|
{
|
|
@@ -187,6 +189,12 @@ const renderActionsCell = (item: ColumnProps, scope: RenderScope<any>) => {
|
|
|
187
189
|
});
|
|
188
190
|
};
|
|
189
191
|
|
|
192
|
+
/** 渲染默认表头标题,避免窄列换行并保留完整标题提示。 */
|
|
193
|
+
const renderHeaderLabel = (item: ColumnProps) => {
|
|
194
|
+
if (!item.label || item.showOverflowTooltip === false) return item.label;
|
|
195
|
+
return h("span", { title: item.label }, item.label);
|
|
196
|
+
};
|
|
197
|
+
|
|
190
198
|
/**
|
|
191
199
|
* 渲染预置列类型,统一收敛图片、状态和操作按钮等通用场景。
|
|
192
200
|
*/
|
|
@@ -227,7 +235,7 @@ const RenderTableColumn = (item: ColumnProps) => {
|
|
|
227
235
|
header: (scope: HeaderRenderScope<any>) => {
|
|
228
236
|
if (item.headerRender) return item.headerRender(scope);
|
|
229
237
|
if (item.prop && slots[`${handleProp(item.prop)}Header`]) return slots[`${handleProp(item.prop)}Header`]!(scope);
|
|
230
|
-
return item
|
|
238
|
+
return renderHeaderLabel(item);
|
|
231
239
|
}
|
|
232
240
|
}
|
|
233
241
|
);
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
>
|
|
73
73
|
<!-- 默认插槽 -->
|
|
74
74
|
<slot />
|
|
75
|
-
<template v-for="item in tableColumns" :key="item">
|
|
75
|
+
<template v-for="(item, index) in tableColumns" :key="item.prop ?? item.type ?? index">
|
|
76
76
|
<!-- selection || radio || index || expand || sort -->
|
|
77
77
|
<el-table-column
|
|
78
78
|
v-if="item.type && columnTypes.includes(item.type)"
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
|
|
134
134
|
<script setup lang="ts" name="ProTable">
|
|
135
135
|
import { ref, watch, provide, onMounted, unref, computed, nextTick, useAttrs, useSlots, isProxy, markRaw, toRaw } from "vue";
|
|
136
|
-
import { ElTable } from "element-plus";
|
|
136
|
+
import { ElTable, type TableInstance } from "element-plus";
|
|
137
137
|
import { useTable } from "../../hooks/useTable";
|
|
138
138
|
import { useSelection } from "../../hooks/useSelection";
|
|
139
139
|
import {
|
|
@@ -171,7 +171,7 @@ const props = withDefaults(defineProps<ProTableProps>(), {
|
|
|
171
171
|
});
|
|
172
172
|
|
|
173
173
|
// table 实例
|
|
174
|
-
const tableRef = ref<
|
|
174
|
+
const tableRef = ref<TableInstance>();
|
|
175
175
|
const attrs = useAttrs();
|
|
176
176
|
const slots = useSlots();
|
|
177
177
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
<script setup lang="ts">
|
|
6
6
|
import { computed } from "vue";
|
|
7
7
|
import DOMPurify from "dompurify";
|
|
8
|
+
import { formatSrc } from "../../utils/utils";
|
|
8
9
|
|
|
9
10
|
defineOptions({
|
|
10
11
|
name: "CoreRichTextPreview"
|
|
@@ -17,7 +18,17 @@ interface RichTextPreviewProps {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
const props = defineProps<RichTextPreviewProps>();
|
|
20
|
-
const sanitizedHtml = computed(() =>
|
|
21
|
+
const sanitizedHtml = computed(() => {
|
|
22
|
+
const sanitized = DOMPurify.sanitize(props.modelValue, { USE_PROFILES: { html: true } });
|
|
23
|
+
if (typeof DOMParser === "undefined") return sanitized;
|
|
24
|
+
|
|
25
|
+
const document = new DOMParser().parseFromString(sanitized, "text/html");
|
|
26
|
+
document.body.querySelectorAll<HTMLElement>("img[src], video[src]").forEach(element => {
|
|
27
|
+
const src = element.getAttribute("src");
|
|
28
|
+
if (src) element.setAttribute("src", formatSrc(src));
|
|
29
|
+
});
|
|
30
|
+
return document.body.innerHTML;
|
|
31
|
+
});
|
|
21
32
|
</script>
|
|
22
33
|
|
|
23
34
|
<style scoped lang="scss">
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<el-button :icon="customIcons[iconValue]" />
|
|
14
14
|
</template>
|
|
15
15
|
</el-input>
|
|
16
|
-
<
|
|
16
|
+
<ProDialog v-model="dialogVisible" :title="placeholder" top="50px" width="760px" :show-footer="false">
|
|
17
17
|
<el-input v-model="inputValue" :placeholder="t('core.icon.search')" size="large" :prefix-icon="Icons.Search" />
|
|
18
18
|
<el-scrollbar v-if="Object.keys(iconsList).length">
|
|
19
19
|
<div class="icon-list">
|
|
@@ -24,13 +24,14 @@
|
|
|
24
24
|
</div>
|
|
25
25
|
</el-scrollbar>
|
|
26
26
|
<el-empty v-else :description="t('core.icon.empty')" />
|
|
27
|
-
</
|
|
27
|
+
</ProDialog>
|
|
28
28
|
</div>
|
|
29
29
|
</template>
|
|
30
30
|
|
|
31
31
|
<script setup lang="ts" name="SelectIcon">
|
|
32
32
|
import { ref, computed, watch } from "vue";
|
|
33
33
|
import * as Icons from "@element-plus/icons-vue";
|
|
34
|
+
import ProDialog from "../../components/Dialog/ProDialog.vue";
|
|
34
35
|
import { useLocaleStore } from "../../locales";
|
|
35
36
|
|
|
36
37
|
const { t } = useLocaleStore();
|
|
@@ -38,7 +38,7 @@ import { computed, inject } from "vue";
|
|
|
38
38
|
import { Delete, Document, Download } from "@element-plus/icons-vue";
|
|
39
39
|
import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
|
|
40
40
|
import type { UploadProps, UploadRequestOptions, UploadUserFile } from "element-plus";
|
|
41
|
-
import { defFileService } from "../../api/base/file";
|
|
41
|
+
import { defFileService } from "../../api/base/v1/file";
|
|
42
42
|
import type { FileInfo } from "../../rpc/base/v1/file";
|
|
43
43
|
import { useLocaleStore } from "../../locales";
|
|
44
44
|
|
|
@@ -171,7 +171,7 @@ async function handleDownload() {
|
|
|
171
171
|
margin-top: 12px;
|
|
172
172
|
background: var(--el-fill-color-light);
|
|
173
173
|
border: 1px solid var(--el-border-color-light);
|
|
174
|
-
border-radius:
|
|
174
|
+
border-radius: var(--admin-page-radius);
|
|
175
175
|
}
|
|
176
176
|
.file-card__main {
|
|
177
177
|
display: flex;
|
|
@@ -44,7 +44,7 @@ import { computed, inject, ref, watch } from "vue";
|
|
|
44
44
|
import { Delete, Document, Download } from "@element-plus/icons-vue";
|
|
45
45
|
import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
|
|
46
46
|
import type { UploadProps, UploadRequestOptions, UploadUserFile } from "element-plus";
|
|
47
|
-
import { defFileService } from "../../api/base/file";
|
|
47
|
+
import { defFileService } from "../../api/base/v1/file";
|
|
48
48
|
import type { FileInfo } from "../../rpc/base/v1/file";
|
|
49
49
|
import { useLocaleStore } from "../../locales";
|
|
50
50
|
|
|
@@ -206,7 +206,7 @@ async function handleDownload(file: UploadUserFile) {
|
|
|
206
206
|
padding: 12px 14px;
|
|
207
207
|
background: var(--el-fill-color-light);
|
|
208
208
|
border: 1px solid var(--el-border-color-light);
|
|
209
|
-
border-radius:
|
|
209
|
+
border-radius: var(--admin-page-radius);
|
|
210
210
|
}
|
|
211
211
|
.file-card__main {
|
|
212
212
|
display: flex;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
:accept="fileType.join(',')"
|
|
16
16
|
>
|
|
17
17
|
<template v-if="imageUrl">
|
|
18
|
-
<img :src="
|
|
18
|
+
<img :src="displayImageUrl" class="upload-image" />
|
|
19
19
|
<div class="upload-handle" @click.stop>
|
|
20
20
|
<div v-if="!self_disabled" class="handle-icon" @click="editImg">
|
|
21
21
|
<el-icon><Edit /></el-icon>
|
|
@@ -43,14 +43,15 @@
|
|
|
43
43
|
<div class="el-upload__tip">
|
|
44
44
|
<slot name="tip"></slot>
|
|
45
45
|
</div>
|
|
46
|
-
<el-image-viewer v-if="imgViewVisible" :url-list="[
|
|
46
|
+
<el-image-viewer v-if="imgViewVisible" :url-list="[displayImageUrl]" @close="imgViewVisible = false" />
|
|
47
47
|
</div>
|
|
48
48
|
</template>
|
|
49
49
|
|
|
50
50
|
<script setup lang="ts" name="UploadImg">
|
|
51
51
|
import { ref, computed, inject } from "vue";
|
|
52
52
|
import { generateUUID } from "../../utils";
|
|
53
|
-
import {
|
|
53
|
+
import { formatSrc } from "../../utils/utils";
|
|
54
|
+
import { defFileService } from "../../api/base/v1/file";
|
|
54
55
|
import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
|
|
55
56
|
import type { UploadProps, UploadRequestOptions } from "element-plus";
|
|
56
57
|
import type { FileInfo } from "../../rpc/base/v1/file";
|
|
@@ -69,7 +70,7 @@ interface UploadFileProps {
|
|
|
69
70
|
fileType?: File.ImageMimeType[]; // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
|
|
70
71
|
height?: string; // 组件高度 ==> 非必传(默认为 150px)
|
|
71
72
|
width?: string; // 组件宽度 ==> 非必传(默认为 150px)
|
|
72
|
-
borderRadius?: string; // 组件边框圆角 ==>
|
|
73
|
+
borderRadius?: string; // 组件边框圆角 ==> 非必传(默认为全局圆角)
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
// 接受父组件参数
|
|
@@ -82,7 +83,7 @@ const props = withDefaults(defineProps<UploadFileProps>(), {
|
|
|
82
83
|
fileType: () => ["image/jpeg", "image/png", "image/gif"],
|
|
83
84
|
height: "150px",
|
|
84
85
|
width: "150px",
|
|
85
|
-
borderRadius: "
|
|
86
|
+
borderRadius: "var(--admin-page-radius)"
|
|
86
87
|
});
|
|
87
88
|
|
|
88
89
|
// 生成组件唯一id
|
|
@@ -98,6 +99,7 @@ const formItemContext = inject(formItemContextKey, void 0);
|
|
|
98
99
|
const self_disabled = computed(() => {
|
|
99
100
|
return props.disabled || formContext?.disabled;
|
|
100
101
|
});
|
|
102
|
+
const displayImageUrl = computed(() => formatSrc(props.imageUrl));
|
|
101
103
|
|
|
102
104
|
/**
|
|
103
105
|
* @description 图片上传
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
</slot>
|
|
24
24
|
</div>
|
|
25
25
|
<template #file="{ file }">
|
|
26
|
-
<img :src="file.url" class="upload-image" />
|
|
26
|
+
<img :src="formatSrc(file.url ?? '')" class="upload-image" />
|
|
27
27
|
<div class="upload-handle" @click.stop>
|
|
28
28
|
<div class="handle-icon" @click="handlePictureCardPreview(file)">
|
|
29
29
|
<el-icon><ZoomIn /></el-icon>
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
<script setup lang="ts" name="UploadImgs">
|
|
47
47
|
import { ref, computed, inject, watch } from "vue";
|
|
48
48
|
import { Plus } from "@element-plus/icons-vue";
|
|
49
|
-
import { defFileService } from "../../api/base/file";
|
|
49
|
+
import { defFileService } from "../../api/base/v1/file";
|
|
50
|
+
import { formatSrc } from "../../utils/utils";
|
|
50
51
|
import type { UploadProps, UploadFile, UploadUserFile, UploadRequestOptions } from "element-plus";
|
|
51
52
|
import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
|
|
52
53
|
import type { FileInfo } from "../../rpc/base/v1/file";
|
|
@@ -66,7 +67,7 @@ interface UploadFileProps {
|
|
|
66
67
|
fileType?: File.ImageMimeType[]; // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
|
|
67
68
|
height?: string; // 组件高度 ==> 非必传(默认为 150px)
|
|
68
69
|
width?: string; // 组件宽度 ==> 非必传(默认为 150px)
|
|
69
|
-
borderRadius?: string; // 组件边框圆角 ==>
|
|
70
|
+
borderRadius?: string; // 组件边框圆角 ==> 非必传(默认为全局圆角)
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
const props = withDefaults(defineProps<UploadFileProps>(), {
|
|
@@ -79,7 +80,7 @@ const props = withDefaults(defineProps<UploadFileProps>(), {
|
|
|
79
80
|
fileType: () => ["image/jpeg", "image/png", "image/gif"],
|
|
80
81
|
height: "150px",
|
|
81
82
|
width: "150px",
|
|
82
|
-
borderRadius: "
|
|
83
|
+
borderRadius: "var(--admin-page-radius)"
|
|
83
84
|
});
|
|
84
85
|
|
|
85
86
|
// 获取 el-form 组件上下文
|
|
@@ -200,7 +201,7 @@ const handleExceed = () => {
|
|
|
200
201
|
const viewImageUrl = ref("");
|
|
201
202
|
const imgViewVisible = ref(false);
|
|
202
203
|
const handlePictureCardPreview: UploadProps["onPreview"] = file => {
|
|
203
|
-
viewImageUrl.value = file.url
|
|
204
|
+
viewImageUrl.value = formatSrc(file.url ?? "");
|
|
204
205
|
imgViewVisible.value = true;
|
|
205
206
|
};
|
|
206
207
|
</script>
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
import { nextTick, computed, inject, shallowRef, onBeforeUnmount } from "vue";
|
|
18
18
|
import { IToolbarConfig, IEditorConfig } from "@wangeditor/editor";
|
|
19
19
|
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
|
|
20
|
-
import { defFileService } from "../../api/base/file";
|
|
20
|
+
import { defFileService } from "../../api/base/v1/file";
|
|
21
|
+
import { normalizeRichTextMediaPaths, normalizeStaticAssetPath } from "../../utils/utils";
|
|
21
22
|
import "@wangeditor/editor/dist/css/style.css";
|
|
22
23
|
import { formContextKey, formItemContextKey } from "element-plus";
|
|
23
24
|
import { useLocaleStore } from "../../locales";
|
|
@@ -41,6 +42,7 @@ interface RichEditorProps {
|
|
|
41
42
|
mode?: "default" | "simple"; // 富文本模式 ==> 非必传(默认为 default)
|
|
42
43
|
hideToolBar?: boolean; // 是否隐藏工具栏 ==> 非必传(默认为false)
|
|
43
44
|
disabled?: boolean; // 是否禁用编辑器 ==> 非必传(默认为false)
|
|
45
|
+
uploadType?: string; // 富文本上传业务类型 ==> 非必传(默认为 content)
|
|
44
46
|
}
|
|
45
47
|
const props = withDefaults(defineProps<RichEditorProps>(), {
|
|
46
48
|
toolbarConfig: () => {
|
|
@@ -56,7 +58,8 @@ const props = withDefaults(defineProps<RichEditorProps>(), {
|
|
|
56
58
|
height: "500px",
|
|
57
59
|
mode: "default",
|
|
58
60
|
hideToolBar: false,
|
|
59
|
-
disabled: false
|
|
61
|
+
disabled: false,
|
|
62
|
+
uploadType: "content"
|
|
60
63
|
});
|
|
61
64
|
const editorMenuConfig = props.editorConfig.MENU_CONF ?? {};
|
|
62
65
|
const resolvedEditorConfig = computed(() => ({
|
|
@@ -84,7 +87,7 @@ const emit = defineEmits<{
|
|
|
84
87
|
}>();
|
|
85
88
|
const valueHtml = computed({
|
|
86
89
|
get() {
|
|
87
|
-
return props.value;
|
|
90
|
+
return normalizeRichTextMediaPaths(props.value);
|
|
88
91
|
},
|
|
89
92
|
set(val: string) {
|
|
90
93
|
// 防止富文本内容为空时,校验失败
|
|
@@ -103,8 +106,8 @@ editorMenuConfig["uploadImage"] = {
|
|
|
103
106
|
async customUpload(file: File, insertFn: InsertFnTypeImg) {
|
|
104
107
|
if (!uploadImgValidate(file)) return;
|
|
105
108
|
try {
|
|
106
|
-
const data = await defFileService.UploadFile(file,
|
|
107
|
-
insertFn(data.url);
|
|
109
|
+
const data = await defFileService.UploadFile(file, props.uploadType);
|
|
110
|
+
insertFn(normalizeStaticAssetPath(data.url));
|
|
108
111
|
} catch (error) {
|
|
109
112
|
console.log(error);
|
|
110
113
|
}
|
|
@@ -127,8 +130,8 @@ editorMenuConfig["uploadVideo"] = {
|
|
|
127
130
|
async customUpload(file: File, insertFn: InsertFnTypeVideo) {
|
|
128
131
|
if (!uploadVideoValidate(file)) return;
|
|
129
132
|
try {
|
|
130
|
-
const data = await defFileService.UploadFile(file,
|
|
131
|
-
insertFn(data.url);
|
|
133
|
+
const data = await defFileService.UploadFile(file, props.uploadType);
|
|
134
|
+
insertFn(normalizeStaticAssetPath(data.url));
|
|
132
135
|
} catch (error) {
|
|
133
136
|
console.log(error);
|
|
134
137
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import pinia from "./stores";
|
|
2
|
+
import { useConfigStore } from "./stores/modules/config";
|
|
3
|
+
|
|
4
|
+
/** setAdminDocumentTitle 使用后端站点名称更新管理端浏览器标题。 */
|
|
5
|
+
export function setAdminDocumentTitle(pageTitle = "") {
|
|
6
|
+
const appTitle = useConfigStore(pinia).display.sysName || import.meta.env.VITE_GLOB_APP_TITLE;
|
|
7
|
+
document.title = pageTitle ? `${pageTitle} - ${appTitle}` : appTitle;
|
|
8
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { formatJson, formatSrc } from "./utils/utils";
|
|
1
|
+
export { formatJson, formatSrc, normalizeRichTextMediaPaths, normalizeStaticAssetPath } from "./utils/utils";
|