create-jnrs-template-vue 1.1.14 → 1.1.15

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.
Files changed (57) hide show
  1. package/bin/create.mjs +55 -32
  2. package/jnrs-template-vue/auto-imports.d.ts +2 -0
  3. package/jnrs-template-vue/components.d.ts +1 -1
  4. package/jnrs-template-vue/package.json +4 -4
  5. package/jnrs-template-vue/src/api/common/index.ts +7 -3
  6. package/jnrs-template-vue/src/api/demos/index.ts +40 -25
  7. package/jnrs-template-vue/src/api/system/index.ts +3 -0
  8. package/jnrs-template-vue/src/assets/images/fileIcon/iconArchive.png +0 -0
  9. package/jnrs-template-vue/src/assets/images/fileIcon/iconAudio.png +0 -0
  10. package/jnrs-template-vue/src/assets/images/fileIcon/iconCode.png +0 -0
  11. package/jnrs-template-vue/src/assets/images/fileIcon/iconExcel.png +0 -0
  12. package/jnrs-template-vue/src/assets/images/fileIcon/iconFile.png +0 -0
  13. package/jnrs-template-vue/src/assets/images/fileIcon/iconFlash.png +0 -0
  14. package/jnrs-template-vue/src/assets/images/fileIcon/iconGif.png +0 -0
  15. package/jnrs-template-vue/src/assets/images/fileIcon/iconImage.png +0 -0
  16. package/jnrs-template-vue/src/assets/images/fileIcon/iconMac.png +0 -0
  17. package/jnrs-template-vue/src/assets/images/fileIcon/iconOfd.png +0 -0
  18. package/jnrs-template-vue/src/assets/images/fileIcon/iconPdf.png +0 -0
  19. package/jnrs-template-vue/src/assets/images/fileIcon/iconPpt.png +0 -0
  20. package/jnrs-template-vue/src/assets/images/fileIcon/iconText.png +0 -0
  21. package/jnrs-template-vue/src/assets/images/fileIcon/iconUnknown.png +0 -0
  22. package/jnrs-template-vue/src/assets/images/fileIcon/iconVideo.png +0 -0
  23. package/jnrs-template-vue/src/assets/images/fileIcon/iconWindows.png +0 -0
  24. package/jnrs-template-vue/src/assets/images/fileIcon/iconWord.png +0 -0
  25. package/jnrs-template-vue/src/assets/images/fileIcon/iconWps.png +0 -0
  26. package/jnrs-template-vue/src/components/base/ImageView.vue +117 -40
  27. package/jnrs-template-vue/src/components/base/JnFileUpload.vue +433 -0
  28. package/jnrs-template-vue/src/components/base/PdfView.vue +106 -0
  29. package/jnrs-template-vue/src/components/common/JnDatetime.vue +37 -0
  30. package/jnrs-template-vue/src/components/common/JnDictTag.vue +70 -0
  31. package/jnrs-template-vue/src/components/common/JnEdit.vue +68 -0
  32. package/jnrs-template-vue/src/layout/SideMenu.vue +1 -0
  33. package/jnrs-template-vue/src/layout/TopHeader.vue +17 -5
  34. package/jnrs-template-vue/src/types/index.ts +32 -8
  35. package/jnrs-template-vue/src/utils/packages.ts +12 -7
  36. package/jnrs-template-vue/src/views/demos/crud/index.vue +180 -24
  37. package/jnrs-template-vue/src/views/demos/unitTest/RequestPage.vue +12 -16
  38. package/jnrs-template-vue/src/views/login/index.vue +13 -18
  39. package/jnrs-template-vue/src/views/system/mine/baseInfo.vue +3 -8
  40. package/jnrs-template-vue/tsconfig.json +4 -1
  41. package/jnrs-template-vue/vite.config.ts +1 -1
  42. package/jnrs-template-vue/viteMockServe/file.ts +68 -0
  43. package/jnrs-template-vue/viteMockServe/fileSrc/mock-pdf.pdf +0 -0
  44. package/jnrs-template-vue/viteMockServe/fileSrc/mock-png-0.png +0 -0
  45. package/jnrs-template-vue/viteMockServe/fileSrc/mock-png-1.png +0 -0
  46. package/jnrs-template-vue/viteMockServe/index.ts +10 -8
  47. package/jnrs-template-vue/viteMockServe/json/dictRes.json +21 -0
  48. package/jnrs-template-vue/viteMockServe/json/loginRes_admin.json +157 -0
  49. package/jnrs-template-vue/viteMockServe/{loginRes_user.json → json/loginRes_user.json} +1 -1
  50. package/jnrs-template-vue/viteMockServe/{tableRes.json → json/tableRes.json} +143 -70
  51. package/jnrs-template-vue/viteMockServe/success.ts +8 -0
  52. package/package.json +1 -1
  53. package/jnrs-template-vue/viteMockServe/dictRes.json +0 -141
  54. package/jnrs-template-vue/viteMockServe/loginRes_admin.json +0 -713
  55. /package/jnrs-template-vue/viteMockServe/{detailsRes.json → json/detailsRes.json} +0 -0
  56. /package/jnrs-template-vue/viteMockServe/{dictItemRes.json → json/dictItemRes.json} +0 -0
  57. /package/jnrs-template-vue/viteMockServe/{roleRes.json → json/roleRes.json} +0 -0
package/bin/create.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { fileURLToPath } from 'url'
2
2
  import { dirname, join, relative } from 'path'
3
3
  import { promises as fs, existsSync } from 'fs'
4
- import { execSync, spawnSync } from 'child_process'
4
+ import { execSync } from 'child_process'
5
5
  import minimist from 'minimist'
6
6
  import prompts from 'prompts'
7
7
 
@@ -9,18 +9,21 @@ const __filename = fileURLToPath(import.meta.url)
9
9
  const __dirname = dirname(__filename)
10
10
 
11
11
  // 检测系统中可用的包管理器
12
- function detectPackageManager() {
12
+ function detectAvailablePackageManagers() {
13
+ const available = []
13
14
  try {
14
- const pnpmVersion = spawnSync('pnpm', ['--version'], { stdio: 'ignore' })
15
- if (pnpmVersion.status === 0) return 'pnpm'
15
+ execSync('pnpm --version', { stdio: 'ignore' })
16
+ available.push('pnpm')
16
17
  } catch {}
17
-
18
18
  try {
19
- const yarnVersion = spawnSync('yarn', ['--version'], { stdio: 'ignore' })
20
- if (yarnVersion.status === 0) return 'yarn'
19
+ execSync('yarn --version', { stdio: 'ignore' })
20
+ available.push('yarn')
21
21
  } catch {}
22
-
23
- return 'npm'
22
+ try {
23
+ execSync('npm --version', { stdio: 'ignore' })
24
+ available.push('npm')
25
+ } catch {}
26
+ return available.length ? available : ['npm']
24
27
  }
25
28
 
26
29
  function getInstallCommand(packageManager) {
@@ -35,13 +38,14 @@ function getInstallCommand(packageManager) {
35
38
  }
36
39
 
37
40
  function getRunCommand(packageManager, script) {
41
+ if (!packageManager || packageManager === 'skip') return `npm run ${script}` // fallback for skip
38
42
  switch (packageManager) {
39
43
  case 'pnpm':
40
- return `${packageManager} ${script}`
41
- case 'npm':
42
- return `${packageManager} run ${script}`
44
+ return `pnpm ${script}`
43
45
  case 'yarn':
44
46
  return `yarn ${script}`
47
+ case 'npm':
48
+ return `npm run ${script}`
45
49
  default:
46
50
  return `npm run ${script}`
47
51
  }
@@ -64,7 +68,6 @@ async function main() {
64
68
  const argv = minimist(process.argv.slice(2), { string: ['_'] })
65
69
  let targetDir = argv._[0]
66
70
 
67
- // 交互式输入项目名
68
71
  if (!targetDir) {
69
72
  const { name } = await prompts({
70
73
  type: 'text',
@@ -83,14 +86,12 @@ async function main() {
83
86
  if (!name) process.exit(1)
84
87
  targetDir = name
85
88
  } else {
86
- // 非交互模式:校验包名和目录
87
89
  if (!isValidPackageName(targetDir)) {
88
90
  console.error('❌ 无效的项目名称:', targetDir)
89
91
  console.error(' 包名称必须有效(小写、无空格等)')
90
92
  process.exit(1)
91
93
  }
92
94
  if (existsSync(join(process.cwd(), targetDir))) {
93
- // ✅ 同步检查
94
95
  console.error(`❌ 目录 "${targetDir}" 已存在!`)
95
96
  process.exit(1)
96
97
  }
@@ -108,23 +109,39 @@ async function main() {
108
109
  pkg.name = toValidPackageName(targetDir)
109
110
  await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2))
110
111
 
111
- // 安装依赖
112
- const packageManager = detectPackageManager()
113
- console.log(`\n📦 检测到包管理器: ${packageManager}`)
114
- console.log(`准备通过 ${packageManager} 安装依赖项中...\n`)
115
-
116
- const [cmd, ...args] = getInstallCommand(packageManager)
117
- try {
118
- execSync(`${cmd} ${args.join(' ')}`, {
119
- cwd: root,
120
- stdio: 'inherit',
121
- env: { ...process.env, FORCE_COLOR: '1' }
122
- })
123
- } catch (e) {
124
- console.error(
125
- `\n⚠️ 安装依赖项失败。请手动运行:\n cd ${relative(process.cwd(), root)} && ${cmd} ${args.join(' ')}`
126
- )
127
- process.exit(1)
112
+ // 检测可用包管理器
113
+ const availablePMs = detectAvailablePackageManagers()
114
+
115
+ // 构建选择项:可用包管理器 + 跳过选项
116
+ const pmChoices = [...availablePMs.map((pm) => ({ title: pm, value: pm })), { title: '跳过安装依赖', value: 'skip' }]
117
+
118
+ const { selected } = await prompts({
119
+ type: 'select',
120
+ name: 'selected',
121
+ message: '请选择包管理器来安装依赖:',
122
+ choices: pmChoices,
123
+ initial: 0
124
+ })
125
+
126
+ if (!selected) process.exit(1)
127
+
128
+ let packageManager = 'npm' // 默认用于生成 dev 命令
129
+ if (selected !== 'skip') {
130
+ packageManager = selected
131
+ console.log(`\n📦 使用 ${packageManager} 安装依赖项中...\n`)
132
+ const [cmd, ...args] = getInstallCommand(packageManager)
133
+ try {
134
+ execSync(`${cmd} ${args.join(' ')}`, {
135
+ cwd: root,
136
+ stdio: 'inherit',
137
+ env: { ...process.env, FORCE_COLOR: '1' }
138
+ })
139
+ } catch (e) {
140
+ console.error(
141
+ `\n⚠️ 安装依赖失败。请手动运行:\n cd ${relative(process.cwd(), root)} && ${cmd} ${args.join(' ')}`
142
+ )
143
+ process.exit(1)
144
+ }
128
145
  }
129
146
 
130
147
  // 成功提示
@@ -133,6 +150,12 @@ async function main() {
133
150
 
134
151
  console.log(`\n✅ 项目创建成功! 👌`)
135
152
  console.log(`\n👉 cd ${relativePath}`)
153
+ if (selected === 'skip') {
154
+ const fallbackPM = availablePMs[0] || 'npm'
155
+ const [cmd, ...args] = getInstallCommand(fallbackPM)
156
+ console.log(` #👉 手动安装依赖:`)
157
+ console.log(` ${cmd} ${args.join(' ')}`)
158
+ }
136
159
  console.log(` ${devCmd}\n`)
137
160
  }
138
161
 
@@ -6,6 +6,8 @@
6
6
  // biome-ignore lint: disable
7
7
  export {}
8
8
  declare global {
9
+ const ElButton: typeof import('element-plus/es').ElButton
9
10
  const ElMessage: typeof import('element-plus/es').ElMessage
10
11
  const ElMessageBox: typeof import('element-plus/es').ElMessageBox
12
+ const ElSwitch: typeof import('element-plus/es').ElSwitch
11
13
  }
@@ -20,6 +20,7 @@ declare module 'vue' {
20
20
  ElCascader: typeof import('element-plus/es')['ElCascader']
21
21
  ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
22
22
  ElContainer: typeof import('element-plus/es')['ElContainer']
23
+ ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
23
24
  ElDatePickerPanel: typeof import('element-plus/es')['ElDatePickerPanel']
24
25
  ElDialog: typeof import('element-plus/es')['ElDialog']
25
26
  ElForm: typeof import('element-plus/es')['ElForm']
@@ -34,7 +35,6 @@ declare module 'vue' {
34
35
  ElOption: typeof import('element-plus/es')['ElOption']
35
36
  ElPagination: typeof import('element-plus/es')['ElPagination']
36
37
  ElPopover: typeof import('element-plus/es')['ElPopover']
37
- ElProgress: typeof import('element-plus/es')['ElProgress']
38
38
  ElSelect: typeof import('element-plus/es')['ElSelect']
39
39
  ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
40
40
  ElSwitch: typeof import('element-plus/es')['ElSwitch']
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jnrs-template-vue",
3
- "version": "1.1.14",
3
+ "version": "1.1.15",
4
4
  "description": "JNRS 信息化管理系统模板",
5
5
  "author": "Talia-Tan",
6
6
  "private": true,
@@ -19,9 +19,9 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@element-plus/icons-vue": "^2.3.2",
22
- "@jnrs/core": "1.1.6",
23
- "@jnrs/shared": "1.1.6",
24
- "@jnrs/vue-core": "1.1.6",
22
+ "@jnrs/core": "1.1.7",
23
+ "@jnrs/shared": "1.1.7",
24
+ "@jnrs/vue-core": "1.1.7",
25
25
  "@vueuse/core": "^14.1.0",
26
26
  "element-plus": "^2.11.9",
27
27
  "pinia": "^3.0.4",
@@ -1,8 +1,7 @@
1
1
  import { request } from '@jnrs/vue-core'
2
- // import { useFetch } from '@vueuse/core'
3
2
 
4
3
  /**
5
- * 文件下载
4
+ * axios 方式下载文件
6
5
  * @param uniqueFileName 文件唯一名称(列表中一般是在 item.attachmentDocument/imageDocument.attachments[].uniqueFileName)
7
6
  * @returns Blob
8
7
  */
@@ -10,10 +9,15 @@ export const FileApi = (uniqueFileName: string): Promise<Blob> => {
10
9
  return request({
11
10
  url: '/api/files/' + uniqueFileName,
12
11
  method: 'get',
13
- responseType: 'blob'
12
+ responseType: 'blob',
13
+ showErrorMsg: false
14
14
  })
15
15
  }
16
16
 
17
+ /**
18
+ * Fetch 方式下载文件
19
+ */
20
+ // import { useFetch } from '@vueuse/core'
17
21
  // export const FileApi = (uniqueFileName: string) => {
18
22
  // return useFetch(`/api/files/${uniqueFileName}`, {
19
23
  // method: 'get',
@@ -1,5 +1,5 @@
1
+ import type { Document } from '@/types'
1
2
  import { request } from '@jnrs/vue-core'
2
- import type { ApiResponse } from '@/types'
3
3
 
4
4
  /**
5
5
  * 404 错误
@@ -23,37 +23,21 @@ export const NoAuth = (showErrorMsg: boolean) => {
23
23
  })
24
24
  }
25
25
 
26
+ interface DetailsParams {
27
+ attachmentDocument: Document
28
+ imageDocument: Document
29
+ }
30
+
26
31
  /**
27
32
  * 数据详情
28
33
  */
29
- export const DetailsApi = (): Promise<ApiResponse> => {
34
+ export const DetailsApi = (): Promise<DetailsParams> => {
30
35
  return request({
31
36
  url: '/details',
32
37
  method: 'get'
33
38
  })
34
39
  }
35
40
 
36
- /**
37
- * 附件(文件)信息
38
- */
39
- export interface Attachment {
40
- id: number
41
- documentId: number
42
- fileName: string
43
- uniqueFileName: string
44
- fileType: string // MIME 类型
45
- fileSize: number // 单位:字节(Bytes)
46
- }
47
-
48
- /**
49
- * 文档容器(用于图片或附件)
50
- */
51
- export interface Document {
52
- id: number
53
- description: string | null
54
- attachments: Attachment[]
55
- }
56
-
57
41
  /**
58
42
  * 项目项
59
43
  */
@@ -64,9 +48,9 @@ export interface ProjectItem {
64
48
  code: string
65
49
  name: string
66
50
  description: string
67
- type: '敏捷型' | '瀑布型' | '混合型'
51
+ projectType?: '敏捷型' | '瀑布型' | '混合型'
68
52
  manager: string
69
- budget: string
53
+ budget: number
70
54
  plannedStartDate: string
71
55
  plannedFinishDate: string
72
56
  progress: string
@@ -75,6 +59,37 @@ export interface ProjectItem {
75
59
  attachmentDocument?: Document // 普通文件类附件
76
60
  }
77
61
 
62
+ /**
63
+ * 新增项目项
64
+ */
65
+ type AddProjectOmitKeys =
66
+ | 'id'
67
+ | 'programCode'
68
+ | 'code'
69
+ | 'manager'
70
+ | 'plannedFinishDate'
71
+ | 'progress'
72
+ | 'status'
73
+ | 'imageDocument'
74
+ | 'attachmentDocument'
75
+
76
+ export type AddProjectItem = Omit<ProjectItem, AddProjectOmitKeys> & {
77
+ id?: string
78
+ newAttachmentFile: []
79
+ newImageFiles: []
80
+ }
81
+
82
+ /**
83
+ * 表单新增
84
+ */
85
+ export const DemosFormApi = (data: FormData) => {
86
+ return request({
87
+ url: '/mock/demos/save',
88
+ method: 'post',
89
+ data
90
+ })
91
+ }
92
+
78
93
  /**
79
94
  * 数据详情
80
95
  */
@@ -16,10 +16,12 @@ interface LoginParams {
16
16
  account: string
17
17
  password: string
18
18
  }
19
+
19
20
  export interface LoginResult extends User {
20
21
  token: string
21
22
  dict: Dict
22
23
  }
24
+
23
25
  export const LoginApi = (data: LoginParams): Promise<LoginResult> => {
24
26
  return request({
25
27
  url: '/api/auth/login',
@@ -50,6 +52,7 @@ interface PasswordChange {
50
52
  userId: number
51
53
  password: string
52
54
  }
55
+
53
56
  export const PasswordChangeApi = (data: PasswordChange) => {
54
57
  return request({
55
58
  url: '/api/auth/change-password',
@@ -1,58 +1,135 @@
1
- <script setup lang="ts">
2
- import { ref, onBeforeUnmount } from 'vue'
1
+ <script lang="ts" setup>
2
+ import type { FileItem } from '@jnrs/shared'
3
+ import type { Attachment } from '@/types'
4
+ import { ref, watch, onActivated, onDeactivated, onBeforeUnmount } from 'vue'
5
+ import { blobToUrl } from '@jnrs/shared'
6
+ import { debounce } from '@jnrs/shared/lodash'
3
7
  import { FileApi } from '@/api/common'
4
8
 
5
- // 响应式数据
6
- const squareUrl = ref<string>('')
9
+ import { JnImageView } from '@jnrs/vue-core/components'
7
10
 
8
- let currentObjectUrl: string | null = null
11
+ interface Props {
12
+ /**
13
+ * 要加载的文件列表 | 文件名唯一标识 uniqueFileName
14
+ */
15
+ loadKeys: Attachment[] | string | undefined
16
+ }
9
17
 
10
- // 加载图片
11
- async function loadImage() {
12
- try {
13
- const blob = await FileApi('20251216134412632-1376c8a7-1f31-451c-9eb4-944aaa03497b.png')
18
+ const { loadKeys } = defineProps<Props>()
14
19
 
15
- // 创建 URL 并保存引用
16
- currentObjectUrl = URL.createObjectURL(blob)
17
- squareUrl.value = currentObjectUrl
18
- } catch (error) {
19
- console.error('加载头像失败:', error)
20
- }
20
+ // 第一张显示的图片
21
+ const posterUrl = ref('')
22
+
23
+ // 存储每个 URL 对应的 URL 对象
24
+ const fileList = ref<FileItem[]>([])
25
+
26
+ // 存储每个 URL 对应的 revoke 函数用于副作用清理
27
+ const revokeFns = ref<(() => void)[]>([])
28
+
29
+ // 清理当前所有 Object URL
30
+ const clearUrls = () => {
31
+ revokeFns.value.forEach((revoke) => revoke())
32
+ revokeFns.value = []
33
+ fileList.value = []
34
+ posterUrl.value = ''
21
35
  }
22
36
 
23
- // 图片加载成功 → 立即释放内存(浏览器已缓存图像)
24
- function handleImageLoad() {
25
- if (currentObjectUrl) {
26
- URL.revokeObjectURL(currentObjectUrl)
27
- currentObjectUrl = null
28
- console.log('✅ 头像加载完成,已释放 Blob URL')
37
+ // 加载文件
38
+ const loadFilesRaw = async (keys: Props['loadKeys']) => {
39
+ clearUrls()
40
+
41
+ if (!keys) {
42
+ return
29
43
  }
30
- }
31
44
 
32
- // 图片加载失败 → 也释放内存
33
- function handleImageError() {
34
- if (currentObjectUrl) {
35
- URL.revokeObjectURL(currentObjectUrl)
36
- currentObjectUrl = null
37
- console.error('❌ 头像加载失败')
45
+ // 如果是字符串
46
+ if (typeof keys === 'string') {
47
+ try {
48
+ const blob = await FileApi(keys)
49
+ const { url, revoke } = blobToUrl(blob)
50
+ fileList.value = [{ src: url, fileName: keys }]
51
+ revokeFns.value = [revoke]
52
+ posterUrl.value = url
53
+ } catch (err) {
54
+ fileList.value = []
55
+ revokeFns.value = []
56
+ posterUrl.value = keys
57
+ console.warn('ImageView 组件异常', err)
58
+ }
59
+ return
38
60
  }
39
- }
40
61
 
41
- // 组件卸载前 → 确保清理(防止用户快速跳转导致未加载完)
42
- onBeforeUnmount(() => {
43
- if (currentObjectUrl) {
44
- URL.revokeObjectURL(currentObjectUrl)
45
- currentObjectUrl = null
46
- console.log('🧹 组件卸载,强制释放 Blob URL')
62
+ // 如果是对象
63
+ if (keys !== null && typeof keys === 'object' && !Array.isArray(keys)) {
64
+ const { uniqueFileName, fileName } = keys
65
+ if (!uniqueFileName) {
66
+ return
67
+ }
68
+ try {
69
+ const blob = await FileApi(uniqueFileName)
70
+ const { url, revoke } = blobToUrl(blob)
71
+
72
+ fileList.value = [{ src: url, fileName: fileName || uniqueFileName }]
73
+ revokeFns.value = [revoke]
74
+ posterUrl.value = url
75
+ } catch (err) {
76
+ fileList.value = []
77
+ revokeFns.value = []
78
+ posterUrl.value = uniqueFileName
79
+ console.warn('ImageView 组件异常', err)
80
+ }
81
+ return
47
82
  }
83
+
84
+ // 如果是数组
85
+ if (Array.isArray(keys) && keys.length > 0) {
86
+ const res = await Promise.all(
87
+ keys.map(async ({ uniqueFileName, fileName }) => {
88
+ if (!uniqueFileName) {
89
+ return { url: '', fileName: '', revoke: () => {} }
90
+ }
91
+ try {
92
+ const blob = await FileApi(uniqueFileName)
93
+ const { url, revoke } = blobToUrl(blob)
94
+ return { url, fileName, revoke }
95
+ } catch (err) {
96
+ console.warn('ImageView 组件异常', err)
97
+ return { url: uniqueFileName, fileName: '', revoke: () => {} }
98
+ }
99
+ })
100
+ )
101
+ fileList.value = res.map(({ url, fileName }) => ({ src: url, fileName }))
102
+ revokeFns.value = res.map((d) => d.revoke)
103
+ // 数组情况下默认显示第一张可用的图片
104
+ posterUrl.value = fileList.value.find((d) => d.fileName)?.src || ''
105
+ }
106
+ }
107
+
108
+ const loadFiles = debounce(loadFilesRaw, 300)
109
+
110
+ watch(
111
+ () => loadKeys,
112
+ (nv) => {
113
+ loadFiles(nv)
114
+ },
115
+ { deep: true, immediate: true }
116
+ )
117
+
118
+ onActivated(() => {
119
+ loadFiles(loadKeys)
120
+ })
121
+
122
+ // 副作用清理
123
+ onDeactivated(() => {
124
+ clearUrls()
48
125
  })
49
126
 
50
- // 启动加载
51
- loadImage()
127
+ // 副作用清理
128
+ onBeforeUnmount(() => {
129
+ clearUrls()
130
+ })
52
131
  </script>
53
132
 
54
133
  <template>
55
- <el-avatar v-if="squareUrl" shape="square" :src="squareUrl" @load="handleImageLoad" @error="handleImageError" />
56
- <!-- 可选:加载中状态 -->
57
- <div v-else>加载中...</div>
134
+ <jn-image-view :src="posterUrl" :previewSrcList="fileList"></jn-image-view>
58
135
  </template>