xiebaomin 1 vuosi sitten
vanhempi
commit
5955211aef
43 muutettua tiedostoa jossa 1153 lisäystä ja 475 poistoa
  1. 5 1
      .env.development
  2. 5 1
      .env.production
  3. 5 0
      components.d.ts
  4. 5 0
      index.html
  5. 40 1
      package-lock.json
  6. 4 1
      package.json
  7. 12 0
      src/Hooks/useFileImage.ts
  8. 4 1
      src/Hooks/usePermission/permiMode.tsx
  9. 7 7
      src/Hooks/usePermission/usePermission.ts
  10. 10 13
      src/Hooks/useStatisticalTime.ts
  11. 65 12
      src/Hooks/useWeChatShare.ts
  12. 25 1
      src/api/context/context.ts
  13. 23 4
      src/components/image-preview/image-preview.vue
  14. 4 0
      src/components/image-preview/index.type.ts
  15. 27 14
      src/components/pdf-preview/pdf-preview.vue
  16. 9 3
      src/router/index.ts
  17. 8 1
      src/router/route.ts
  18. 3 1
      src/store/tab/tab.ts
  19. 2 1
      src/store/tab/tab.type.ts
  20. 10 11
      src/store/user/user.ts
  21. 0 1
      src/store/user/user.type.ts
  22. 62 5
      src/types/api/context.type.ts
  23. 7 3
      src/types/api/user.type.ts
  24. 10 0
      src/types/views/add.type.ts
  25. 11 0
      src/types/views/database.type.ts
  26. 35 2
      src/util/authStorage.ts
  27. 7 2
      src/util/http.ts
  28. 107 0
      src/views/Database/add.vue
  29. 45 24
      src/views/Database/components/context-contain.vue
  30. 37 18
      src/views/Database/components/context-head.vue
  31. 44 35
      src/views/Database/components/context-share.vue
  32. 40 24
      src/views/Database/components/file-content.vue
  33. 18 10
      src/views/Database/components/high-light-text.vue
  34. 4 4
      src/views/Database/components/scroll-tap-btn.vue
  35. 95 75
      src/views/Database/components/search-head.vue
  36. 46 42
      src/views/Database/components/text-file-content.vue
  37. 11 10
      src/views/Database/components/video-img-content.vue
  38. 97 39
      src/views/Database/data-all/index.vue
  39. 90 69
      src/views/Database/detail.vue
  40. 22 24
      src/views/Database/index.vue
  41. 4 3
      src/views/Login/index.vue
  42. 78 9
      src/views/Preview/index.vue
  43. 10 3
      src/views/error/404.vue

+ 5 - 1
.env.development

@@ -2,6 +2,10 @@ VITE_ENV = 'development'
 
 VITE_BASE_URL = 'https://core-b.caimei365.com'
 
+VITE_BASE_HTTP = 'https://material-b.caimei365.com'
+
 VITE_BASE_OSS = 'https://caimei-oss.oss-cn-shenzhen.aliyuncs.com/beta/archiveFile/'
 
-VITE_IMAGE_URL = 'https://static.caimei365.com/'
+VITE_IMAGE_URL = 'https://static.caimei365.com/'
+
+VITE_HTTP_URL = 'http://120.79.25.27:8009'

+ 5 - 1
.env.production

@@ -2,4 +2,8 @@ VITE_ENV = 'production'
 
 VITE_BASE_URL = 'https://core.caimei365.com'
 
-VITE_BASE_OSS = 'https://caimei-oss.oss-cn-shenzhen.aliyuncs.com/beta/archiveFile/'
+VITE_BASE_HTTP = 'https://material.caimei365.com'
+
+VITE_BASE_OSS = 'https://caimei-oss.oss-cn-shenzhen.aliyuncs.com/beta/archiveFile/'
+
+VITE_HTTP_URL = 'https://www.caimei365.com'

+ 5 - 0
components.d.ts

@@ -8,6 +8,9 @@ export {}
 declare module 'vue' {
   export interface GlobalComponents {
     DataButton: typeof import('./src/components/data-button/data-button.vue')['default']
+    ElForm: typeof import('element-plus/es')['ElForm']
+    ElFormItem: typeof import('element-plus/es')['ElFormItem']
+    ElInput: typeof import('element-plus/es')['ElInput']
     ImagePreview: typeof import('./src/components/image-preview/image-preview.vue')['default']
     PdfPreview: typeof import('./src/components/pdf-preview/pdf-preview.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
@@ -24,6 +27,8 @@ declare module 'vue' {
     VanHighlight: typeof import('vant/es')['Highlight']
     VanImage: typeof import('vant/es')['Image']
     VanSearch: typeof import('vant/es')['Search']
+    VanSwipe: typeof import('vant/es')['Swipe']
+    VanSwipeItem: typeof import('vant/es')['SwipeItem']
     VanTab: typeof import('vant/es')['Tab']
     VanTabs: typeof import('vant/es')['Tabs']
     VideoPreview: typeof import('./src/components/video-preview/video-preview.vue')['default']

+ 5 - 0
index.html

@@ -6,6 +6,11 @@
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>Vite + Vue + TS</title>
+    <style>
+      #app {
+        width: 100vw;
+      }
+    </style>
   </head>
   <body>
     <div id="app"></div>

+ 40 - 1
package-lock.json

@@ -25,11 +25,13 @@
         "vue3-pdfjs": "^0.1.6"
       },
       "devDependencies": {
+        "@types/crypto-js": "^4.2.1",
         "@types/node": "^20.10.0",
         "@types/video.js": "^7.3.56",
         "@vitejs/plugin-vue": "^4.5.0",
         "@vitejs/plugin-vue-jsx": "^3.1.0",
         "@vueuse/core": "^10.7.1",
+        "crypto-js": "^4.2.0",
         "postcss-px-to-viewport": "^1.1.1",
         "sass": "^1.69.5",
         "typescript": "^5.2.2",
@@ -37,7 +39,8 @@
         "unplugin-vue-components": "^0.25.2",
         "video.js": "^8.6.1",
         "vite": "^5.0.0",
-        "vue-tsc": "^1.8.22"
+        "vue-tsc": "^1.8.22",
+        "weixin-js-sdk": "^1.6.5"
       }
     },
     "node_modules/@ampproject/remapping": {
@@ -1305,6 +1308,12 @@
         "win32"
       ]
     },
+    "node_modules/@types/crypto-js": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.2.1.tgz",
+      "integrity": "sha512-FSPGd9+OcSok3RsM0UZ/9fcvMOXJ1ENE/ZbLfOPlBWj7BgXtEAM8VYfTtT760GiLbQIMoVozwVuisjvsVwqYWw==",
+      "dev": true
+    },
     "node_modules/@types/estree": {
       "version": "1.0.5",
       "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz",
@@ -2115,6 +2124,12 @@
         "node": ">=10"
       }
     },
+    "node_modules/crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+      "dev": true
+    },
     "node_modules/css-in-js-utils": {
       "version": "3.1.0",
       "resolved": "https://registry.npmmirror.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz",
@@ -4165,6 +4180,12 @@
       "integrity": "sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==",
       "dev": true
     },
+    "node_modules/weixin-js-sdk": {
+      "version": "1.6.5",
+      "resolved": "https://registry.npmmirror.com/weixin-js-sdk/-/weixin-js-sdk-1.6.5.tgz",
+      "integrity": "sha512-Gph1WAWB2YN/lMOFB/ymb+hbU/wYazzJgu6PMMktCy9cSCeW5wA6Zwt0dpahJbJ+RJEwtTv2x9iIu0U4enuVSQ==",
+      "dev": true
+    },
     "node_modules/whatwg-fetch": {
       "version": "3.6.19",
       "resolved": "https://registry.npmmirror.com/whatwg-fetch/-/whatwg-fetch-3.6.19.tgz",
@@ -5014,6 +5035,12 @@
       "integrity": "sha512-QqmCsydHS172Y0Kc13bkMXvipbJSvzeglBncJG3LsYJSiPlxYACz7MmJBs4A8l1oU+jfhYEIC/+AUSlvjmiX/g==",
       "optional": true
     },
+    "@types/crypto-js": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.2.1.tgz",
+      "integrity": "sha512-FSPGd9+OcSok3RsM0UZ/9fcvMOXJ1ENE/ZbLfOPlBWj7BgXtEAM8VYfTtT760GiLbQIMoVozwVuisjvsVwqYWw==",
+      "dev": true
+    },
     "@types/estree": {
       "version": "1.0.5",
       "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz",
@@ -5682,6 +5709,12 @@
         "yaml": "^1.10.0"
       }
     },
+    "crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+      "dev": true
+    },
     "css-in-js-utils": {
       "version": "3.1.0",
       "resolved": "https://registry.npmmirror.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz",
@@ -7270,6 +7303,12 @@
       "integrity": "sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==",
       "dev": true
     },
+    "weixin-js-sdk": {
+      "version": "1.6.5",
+      "resolved": "https://registry.npmmirror.com/weixin-js-sdk/-/weixin-js-sdk-1.6.5.tgz",
+      "integrity": "sha512-Gph1WAWB2YN/lMOFB/ymb+hbU/wYazzJgu6PMMktCy9cSCeW5wA6Zwt0dpahJbJ+RJEwtTv2x9iIu0U4enuVSQ==",
+      "dev": true
+    },
     "whatwg-fetch": {
       "version": "3.6.19",
       "resolved": "https://registry.npmmirror.com/whatwg-fetch/-/whatwg-fetch-3.6.19.tgz",

+ 4 - 1
package.json

@@ -27,11 +27,13 @@
     "vue3-pdfjs": "^0.1.6"
   },
   "devDependencies": {
+    "@types/crypto-js": "^4.2.1",
     "@types/node": "^20.10.0",
     "@types/video.js": "^7.3.56",
     "@vitejs/plugin-vue": "^4.5.0",
     "@vitejs/plugin-vue-jsx": "^3.1.0",
     "@vueuse/core": "^10.7.1",
+    "crypto-js": "^4.2.0",
     "postcss-px-to-viewport": "^1.1.1",
     "sass": "^1.69.5",
     "typescript": "^5.2.2",
@@ -39,6 +41,7 @@
     "unplugin-vue-components": "^0.25.2",
     "video.js": "^8.6.1",
     "vite": "^5.0.0",
-    "vue-tsc": "^1.8.22"
+    "vue-tsc": "^1.8.22",
+    "weixin-js-sdk": "^1.6.5"
   }
 }

+ 12 - 0
src/Hooks/useFileImage.ts

@@ -0,0 +1,12 @@
+const getFileImg = (str: string): { img: string } => {
+	const img = str?.indexOf(".pdf")
+	? "https://static.caimei365.com/app/mini-database/H5-pdf.png"
+	: str?.indexOf(".docx")
+	? "https://static.caimei365.com/app/mini-database/H5-doc.png"
+	: "https://static.caimei365.com/app/mini-database/H5-ppt.png"
+	return {
+		img
+	}
+}
+
+export default getFileImg

+ 4 - 1
src/Hooks/usePermission/permiMode.tsx

@@ -3,6 +3,7 @@ import type { IPermiObj } from './type'
 import { css } from '@emotion/css'
 import 'vant/lib/index.css';
 import { Button } from 'vant';
+import { useRoute } from 'vue-router';
 
 const MyComponent = (prop: IPermiObj | Boolean): ComponentOptions =>
 	defineComponent({
@@ -45,9 +46,11 @@ const MyComponent = (prop: IPermiObj | Boolean): ComponentOptions =>
 				background-color: #FF5B00;
 				color: #FFF;
 			`
+			const route = useRoute()
 			const handleClick = () => {
 				//window.localStorage.setItem('permission', '0')
-				window.open('https://www.caimei365.com/')
+				//window.open(import.meta.env.VITE_HTTP_URL + obj.redirect + '?t=' + route.query.t + '&isDataBase=' + 1 + '&spId=')
+				window.location.href = 'http://localhost:8009/login.html' + '?t=' + route.query.t + '&isDataBase=' + 1 + '&spId='
 			}
 			const pageLocation = ref<number>(0)
 			const stop = () => {

+ 7 - 7
src/Hooks/usePermission/usePermission.ts

@@ -4,15 +4,15 @@ import MyComponent from './permiMode.tsx'
 //用户权限  0可查看,1未登录,2需升级会员机构,3需升级医美会员机构,4需要抵扣采美豆,5无权限查看
 const permissionText: IPermi = [
 	false,
-	{ text: '登录后查看完整内容', btnText: '去登录', redirect: '/pages/login/login' },
-	{ text: '医美资质机构可查看完整内容', btnText: '去升级', redirect: '/pages/login/apply' },
-	{ text: '医美资质机构可查看完整内容', btnText: '去升级', redirect: '/pages/login/apply' },
-	{ text: '医美资质机构可查看完整内容', btnText: '去查看', redirect: '' },
-	{ text: '无权限查看!', btnText: '确认' },
+	{text: '登录后查看完整内容', btnText: '登录', redirect: '/login.html' },
+	{text: '请升级成为会员机构后方可查看!', btnText: '升级会员', redirect: '/user/setting/upgrade.html' },
+	{text: '医美资质机构可查看完整内容', btnText: '升级医美资质', redirect: '/user/setting/upgrade.html' },
+	{text: '需抵扣100采美豆方可查看!', btnText: '去查看', redirect: '' },
+	{text: '登录后查看完整内容', btnText: '登录', redirect: '/login.html' }
 ]
 
-export const usePermission = (): UsePermission => {
-	const permi = (): number => window.sessionStorage.getItem('permission') !== null ? Number(window.sessionStorage.getItem('permission')) : 1
+export const usePermission = (ps?: number): UsePermission => {
+	const permi = (): number => ps ? Number(ps) : 5
 	const isPermi = (): Boolean | IPermiObj => permissionText[permi()]
 	const cE = MyComponent(isPermi())
 	return {

+ 10 - 13
src/Hooks/useStatisticalTime.ts

@@ -2,7 +2,7 @@
  * 移动端时间统计 hook
  */
 
-import { onMounted, onUnmounted } from "vue"
+import { onMounted } from "vue"
 
 
 const useStatisticalTime = (callback: (timing: number) => void) => {
@@ -15,35 +15,32 @@ const useStatisticalTime = (callback: (timing: number) => void) => {
 		localStorage.setItem('timing', startTime)
 	}
 
-	const endTiming = async () => {
+	const endTiming = () => {
 		const startTime = Number(localStorage.getItem('timing'))
 		const endTime = new Date().valueOf()
 		if (startTime) {
 			console.log('统计时间结束', startTime, endTime)
 			const timing = endTime - startTime
-			await callback(timing)
+			callback(timing)
 			localStorage.removeItem('startTime')
 		}
 	}
 
 	onMounted(() => {
 		startTiming()
-		window.addEventListener('pagehide', async () => {
+		window.addEventListener('pagehide', () => {
 			console.log('页面关闭')
-			await endTiming()
-		})
+			endTiming()
+		}, false)
 	})
 	
-	onUnmounted(() => {
-		window.addEventListener('pagehide', async () => {
+
+	return () => {
+		window.removeEventListener('pagehide', () => {
 			console.log('页面关闭')
-			await endTiming()
+			endTiming()
 		})
 		localStorage.removeItem('timing')
-	})
-
-	return () => {
-		window.removeEventListener('pagehide', () => {})
 	}
 }
 

+ 65 - 12
src/Hooks/useWeChatShare.ts

@@ -1,4 +1,8 @@
+import { getShareConfig } from '@/api/context/context';
 import { DFindParams, DScrollTab, ChangeTabEmit } from '@/types/views/database.type';
+import wx from 'weixin-js-sdk'
+import { IShareConfig } from '../types/api/context.type';
+import { showToast } from 'vant';
 
 type shareOptions = {
 	type: ChangeTabEmit,
@@ -6,11 +10,19 @@ type shareOptions = {
 	text: `您的好友给你分享了${DFindParams<DScrollTab, 'text'>}链接`
 }
 
+const config: {
+	appId: DFindParams<IShareConfig, 'appId'>
+	jsApiList: wx.ApiMethod[]
+} = {
+	appId: 'wx91c4152b60ca91a3', // 微信公众号appId
+	jsApiList: ['updateAppMessageShareData', 'onMenuShareAppMessage', 'updateTimelineShareData', 'onMenuShareTimeline'], // 微信配置可用权限
+}
+export type shareParams = { type: ChangeTabEmit | string, id: string, spId?: number, userId?: number, suid?: number, link?: string }
 const shareOptions: shareOptions[] = [
 	{
 		type: '1',
 		imageUrl: '',
-		text: '您的好友给你分享了文件链接'
+		text: '您的好友给你分享了图片链接'
 	},
 	{
 		type: '2',
@@ -20,29 +32,70 @@ const shareOptions: shareOptions[] = [
 	{
 		type: '3',
 		imageUrl: '',
-		text: '您的好友给你分享了图片链接'
+		text: '您的好友给你分享了文件链接'
 	},
 	{
-		type: '4',
+		type: '5',
 		imageUrl: '',
-		text: '您的好友给你分享了文链接'
+		text: '您的好友给你分享了文链接'
 	},
 	{
-		type: '5',
+		type: '6',
 		imageUrl: '',
-		text: '您的好友给你分享了百科链接'
+		text: '您的好友给你分享了文章链接'
 	},
 	{
-		type: '6',
+		type: '7',
 		imageUrl: '',
-		text: '您的好友给你分享了文本链接'
+		text: '您的好友给你分享了百科链接'
 	}
 ]
 
-const useWeChatShare = () => {
-	
-
-	return {}
+const useWeChatShare = async (params: shareParams) => {
+	const isWeChat = () => /MicroMessenger/i.test(navigator.userAgent)
+	const linkParams =
+		import.meta.env.VITE_HTTP_URL + (Number(params.type) === 7
+			? "/encyclopedia/detail-"
+			: "/info/detail-") + params.id + ".html";
+	if (!isWeChat()) return showToast('请在微信浏览器中分享')
+	const { data } = await getShareConfig({
+		appId: 'wx91c4152b60ca91a3',
+		url: location.href
+	})
+	console.log(data)
+	wx.config({
+		debug: false,
+		appId: config.appId,
+		nonceStr: data?.noncestr!,
+		timestamp: data?.timestamp!,
+		signature: data?.signature!,
+		jsApiList: config.jsApiList
+	})
+	const obj = shareOptions.filter(e => e.type === params.type)[0] || {}
+	const link = Number(params.type) < 5 ? import.meta.env.VITE_BASE_HTTP + `/preview?t=${params.type}&id=${params.id}&uid=${params.userId || ''}&suid=${params.suid || ''}&spId=${params.spId || ''}` : linkParams
+	const shareConfig: wx.IonMenuShareAppMessage = {
+		title: obj.text,
+		desc: obj.text,
+		link,
+		imgUrl: obj.imageUrl || '',
+		success: () => {
+			console.log(obj, '成功')
+		},
+		fail: () => {
+			console.log('失败', obj, location.href)
+		},
+		cancel: () => {
+			console.log('取消了')
+		}
+	}
+	showToast('请点击右上角...进行分享')
+	wx.ready(() => {
+		wx.onMenuShareAppMessage(shareConfig)
+		wx.onMenuShareTimeline(shareConfig)
+	})
+	wx.error(function (err) {
+		console.warn(err)
+	})
 }
 
 export default useWeChatShare

+ 25 - 1
src/api/context/context.ts

@@ -7,8 +7,14 @@ import type {
 	DStoreResultData,
 	IStoreRequest,
 	IArchiveForm,
+	IPreviewRequest,
+	IShareConfig,
+	DShareConfigResultData,
+	IStatistics,
+	DPreviewResultData
 } from '@/types/api/context.type'
-import { requestGET } from '@/types/api/index'
+import { requestGET, requestPOST } from '@/types/api/index'
+import { DForm } from '@/types/views/add.type'
 
 export const getLabelKeyList = (data: ILabelKeyRequest) =>
 	requestGET<ContextRequest, DLabelKeyResultData>('/commodity/product/archive/findKeyWordList', data)
@@ -21,3 +27,21 @@ export const getStoreList = (data: IStoreRequest) =>
 
 export const getArchiveFormDetails = (data: IArchiveForm) =>
 	requestGET<ContextRequest, DArchiveResultData>('/commodity/product/archive/from', data)
+
+export const getShareConfig = (data: IShareConfig) =>
+	requestGET<ContextRequest, DShareConfigResultData>('https://zplma.caimei365.com/wx/sdk/config/data', data)
+
+export const setStatistics = (data: IStatistics) => 
+	requestGET<ContextRequest, { data: string }>('/user/record/Statistics', data)
+
+export const addAllStatusForm = (data: DForm) =>
+	requestPOST<ContextRequest, { data: string }>('/commodity/product/archive/add', data)
+
+export const previewData = (params: IPreviewRequest) => {
+	return new Promise<DPreviewResultData>((resolve, reject) => {
+		const { userId, type, id } = params
+		fetch(`${import.meta.env.VITE_BASE_URL}/commodity/product/archive/${userId}/${type}/${id}/details`).then(res => res.json()).then(data => {
+			resolve(data)
+		}).catch(err => reject(err))
+	})
+}

+ 23 - 4
src/components/image-preview/image-preview.vue

@@ -1,13 +1,32 @@
 <template>
-	<div>
-		图片预览
+	<div class="imagePreview">
+		<van-swipe :autoplay="3000" lazy-render>
+			<van-swipe-item v-for="image,index in urls" :key="index">
+				<img :src="image" style="width: 100%;" />
+			</van-swipe-item>
+		</van-swipe>
 	</div>
 </template>
 
 <script setup lang="ts">
 
-</script>
+import { IImagePreviewList } from './index.type';
+
+defineProps<IImagePreviewList>()
 
-<style scoped>
 
+</script>
+
+<style scoped lang="scss">
+.imagePreview {
+	padding: 80px 20px;
+	width: 100%;
+	box-sizing: border-box;
+	height: 100vh;
+}
+:deep() {
+	.van-swipe,.van-swipe-item,.van-swipe__track {
+		width: 100%;
+	}
+}
 </style>

+ 4 - 0
src/components/image-preview/index.type.ts

@@ -0,0 +1,4 @@
+export type IImagePreviewList = {
+	urls: string[];
+	isPermi: boolean;
+}

+ 27 - 14
src/components/pdf-preview/pdf-preview.vue

@@ -1,29 +1,42 @@
 <template>
-	<pdf-view :source="state.source!" :page="i" v-for="i, index in state.numPages" :key="index"/>
+  <pdf-view
+    :source="state.source!"
+    :page="i"
+    v-for="i,index in state.numPages"
+    :key="index"
+  />
 </template>
 
 <script setup lang="ts">
-import PdfView from 'vue-pdf-embed'
-import { createLoadingTask } from 'vue3-pdfjs'
-import { onMounted, reactive } from "vue";
+import PdfView from "vue-pdf-embed";
+import { createLoadingTask } from "vue3-pdfjs";
+import { reactive, onMounted } from 'vue';
 import type { IProps, IPDFPreview } from "./index.type";
 
 const props = defineProps<IProps>();
 
+console.log(props);
+
 const state = reactive<IPDFPreview>({
-	source: props.url,
-	scale: 1,
-	pageNum: 1,
-	numPages: 0 // 总页数
-})
+  source: props.url,
+  scale: 1,
+  pageNum: 1,
+  numPages: 0, // 总页数
+});
+
+const initPDF = () => {
+  console.log("PDF链接", state.source);
+  const loadingTask = createLoadingTask(state.source!);
+  loadingTask.promise
+      .then((pdf: Pick<IPDFPreview, "numPages">) => {
+        console.log("pdf", pdf);
+        state.numPages = pdf.numPages;
+      })
+};
 
 onMounted(() => {
-	const loadingTask = createLoadingTask(state.source!)
-	loadingTask.promise.then((pdf: Pick<IPDFPreview, "numPages">) => {
-		state.numPages = props.isPermi ? pdf.numPages : 5
-	})
+	initPDF()
 })
-
 </script>
 
 <style scoped></style>

+ 9 - 3
src/router/index.ts

@@ -1,8 +1,9 @@
 import { createWebHistory, createRouter } from 'vue-router'
 import { staticRouter, notFoundPower } from './route'
-import { getStorage } from '@/util/authStorage'
+
 import NProgress from 'nprogress'
 import 'nprogress/nprogress.css'
+import { useUserInfoState } from '@/store/user/user';
 
 const router = createRouter({
 	history: createWebHistory(),
@@ -13,15 +14,20 @@ NProgress.configure({
   showSpinner: false
 })
 
-const whiteList: any = ['/database/index', '/login', '/database/other', '/database/detail']
+const whiteList: string[] = ['/login', '/preview']
 
 router.beforeEach((to, _, next) => {
+	const { token } = useUserInfoState()
 	console.log(to)
 	NProgress.start()
 	if (to.meta.title) {
 		document.title = to.meta.title as string
 	}
-	if (getStorage() && to.path === '/login') {
+	if (token) {
+		if (to.path === '/login') {
+			next('/database/index?t=0')
+			return
+		}
 		next()
 	} else {
     // 没有token

+ 8 - 1
src/router/route.ts

@@ -68,7 +68,6 @@ export const staticRouter: Array<RouteRecordRaw> = [
 	{
 		path: '/database',
 		name: 'database',
-		redirect: '/database/index',
 		component: () => import('@/views/Database/index.vue'),
 		children: [
 			{
@@ -97,5 +96,13 @@ export const staticRouter: Array<RouteRecordRaw> = [
 		meta: {
 			title: '文件详情',
 		}
+	},
+	{
+		path: '/database/add',
+		name: 'Add',
+		component: () => import('@/views/Database/add.vue'),
+		meta: {
+			title: '新增个人话术'
+		}
 	}
 ]

+ 3 - 1
src/store/tab/tab.ts

@@ -7,7 +7,8 @@ export const useTabStore = defineStore('myTabStore', {
 		stageStatus: '',
 		productId: 0,
 		labelIds: '',
-		searchValue: ''
+		searchValue: '',
+		allStatus: '1'
 	}),
 	actions: {
 		updatedTabStore(step: Partial<DTabStore>) {
@@ -16,6 +17,7 @@ export const useTabStore = defineStore('myTabStore', {
 			this.productId = step?.productId
 			this.labelIds = step?.labelIds
 			this.searchValue = step?.searchValue!
+			this.allStatus = step?.allStatus!
 		}
 	},
 	persist: true

+ 2 - 1
src/store/tab/tab.type.ts

@@ -1,5 +1,5 @@
 import { IArchiveRequest } from "@/types/api/context.type"
-import { DFindParams, DScrollTab } from "@/types/views/database.type"
+import { DFindParams, DScrollTab, IAllStatus } from "@/types/views/database.type"
 
 export type DTabStore = {
 	type: DFindParams<DScrollTab, 'type'>
@@ -7,4 +7,5 @@ export type DTabStore = {
 	stageStatus: DFindParams<IArchiveRequest, 'stageStatus'>
 	productId: DFindParams<IArchiveRequest, 'productId'>
 	searchValue: string
+	allStatus: DFindParams<IAllStatus, 'allStatus'>
 }

+ 10 - 11
src/store/user/user.ts

@@ -1,27 +1,26 @@
 import { defineStore } from "pinia";
-import { getStorage } from "@/util/authStorage"
-import type { UserInfo, UserInfoState } from './user.type';
 import { login } from '@/api/user/login';
 import { ILogin, ILoginRequestData } from '@/types/api/user.type';
 import { IResultData } from '@/types/api/index';
 
 
 export const useUserInfoState = defineStore('userInfo', {
-	state: (): UserInfoState<UserInfo> => ({
-		userInfo: {},
-		permission: 1
+	state: (): ILoginRequestData => ({
+		token: '',
+		userId: '',
+		serviceProviderId: '',
+		mobile: '',
+		name: ''
 	}),
 	actions: {
-		async setUserInfo() {
-			if (getStorage()) {
-				this.userInfo = getStorage() as UserInfo
-			}
-		},
+
 		login(data: ILogin): Promise<IResultData<ILoginRequestData>> {
 			return new Promise(async (resolve, reject) => {
 				try {
 					const res = await login(data)
-					//this.permission = res.data!.permission || 1
+					this.token = res.data?.token!
+					this.serviceProviderId = res.data?.serviceProviderId!
+					this.userId = res.data?.userId!
 					resolve(res)
 				} catch (error) {
 					reject(error);

+ 0 - 1
src/store/user/user.type.ts

@@ -1,6 +1,5 @@
 export interface UserInfoState<T> {
 	userInfo: T
-	permission: number | string
 }
 
 export interface UserInfo {

+ 62 - 5
src/types/api/context.type.ts

@@ -1,12 +1,23 @@
+import { DForm } from "../views/add.type";
 import { ChangeTabEmit } from "../views/database.type";
-import { DResultListData, IGlobalListRequest } from "./index";
+import { DResultListData, IGlobalListRequest, IResultData } from "./index";
 
+export type IPreviewResult = {
+	fileName: string
+	fileUrl: string
+}
 
 export type ILabelKeyResultData = { // 标签返回
 	v: string;
 	k: string;
 }
 
+export type DWaters = {
+	ossUrl: string;
+	ossName: string;
+	fileName: string;
+}
+
 export interface DArchiveResultData extends IArchiveRequest { // 列表返回
 	addTime?: string
 	content?: string
@@ -14,11 +25,12 @@ export interface DArchiveResultData extends IArchiveRequest { // 列表返回
 	image?: string
 	productType?: string
 	shopName?: string
-	waters?: Array<string>
+	waters?: Array<string> | string
 	productArchiveId?: number
 	productClassify?: number
 	keywords?: string
 	pv?: string
+	link?: string
 }
 
 export type IStoreResultData = { // 商品返回类型
@@ -29,10 +41,25 @@ export type IStoreResultData = { // 商品返回类型
 	shopName: string
 } 
 
+export type DShareConfigResultData = {
+	appId?: string
+	timestamp: number
+	noncestr: string
+	signature: string
+}
+
 export type DLabelKeyResultData = DResultListData<ILabelKeyResultData> 
 
 export type DStoreResultData = DResultListData<IStoreResultData>
 
+export type IPreviewForm = {
+	permission: number
+	fileArchiveList: IPreviewResult[]
+	waters: Array<string> | string
+	productId: string
+	id?: string
+}
+export type DPreviewResultData = IResultData<IPreviewForm>
 export interface ILabelKeyRequest extends IGlobalListRequest { // 参数
 	v: string;
 }
@@ -40,13 +67,14 @@ export interface ILabelKeyRequest extends IGlobalListRequest { // 参数
 export interface IArchiveRequest { // 参数
 	title?: string
 	userId?: string
-	type?: ChangeTabEmit | null
-	productId?: number | null
+	type?: ChangeTabEmit | string
+	productId?: number | string
 	stageStatus?: string | null
-	allStatus?: string | null
+	allStatus?: string | number | null
 	labelId?: string
 	labelIds?: string
 	spId?: string
+	pageNum?: number
 }
 
 export type IArchiveListRequest = IArchiveRequest & Partial<IGlobalListRequest>
@@ -59,7 +87,33 @@ export interface IStoreRequest extends IGlobalListRequest {
 
 export type IArchiveForm = {
 	id: string
+	userId?: string
+	spId?: string
+}
+
+export type IPreviewRequest = {
+	userId: string
+	id: string
+	type: ChangeTabEmit
+}
+
+export type IShareConfig = {
+	appId: 'wx91c4152b60ca91a3', // 微信公众号appId
+	url: string
+}
+
+export type IStatistics = {
+	accessDuration: number
+	pageType?: '67' | string
+	pageLabel?: '内容库文件预览' | string
+	pagePath: string
+	headUserId?: string
+	productArchiveId?: string
+	accessClient: '0' | '1'
 	userId: string
+	productId: string
+	shopId: string
+	behaviorType: '1'
 }
 
 export interface ContextRequest {
@@ -67,4 +121,7 @@ export interface ContextRequest {
 	'/commodity/product/archive/list': IArchiveListRequest
 	'/commodity/product/archive/findProductList': IStoreRequest
 	'/commodity/product/archive/from': IArchiveForm
+	'https://zplma.caimei365.com/wx/sdk/config/data': IShareConfig
+	'/user/record/Statistics': IStatistics
+	'/commodity/product/archive/add': DForm
 }

+ 7 - 3
src/types/api/user.type.ts

@@ -1,7 +1,8 @@
 export type ILogin = {
 	mobileOrEmail: string;
 	password: string;
-	isUnion: 1
+	isUnion: 1,
+	unionId: string
 }
 
 export type IUserInfo = {
@@ -9,8 +10,11 @@ export type IUserInfo = {
 }
 
 export type ILoginRequestData = {
-	token?: string
-	permission?: string | number;
+	token: string
+	mobile: string
+	name: string
+	userId: number | string
+	serviceProviderId: number | string
 }
 
 export interface LoginRequest {

+ 10 - 0
src/types/views/add.type.ts

@@ -0,0 +1,10 @@
+import { ChangeTabEmit } from './database.type';
+
+export type DForm = {
+	title: string
+	content: string
+	id?: string
+	spId?: number
+	userId?: string
+	type: ChangeTabEmit
+}

+ 11 - 0
src/types/views/database.type.ts

@@ -27,6 +27,7 @@ export type DetailParams = {
   type: ChangeTabEmit
   pageNum?: number
   content?: string
+  allStatus?: DFindParams<IAllStatus, "allStatus">
 }
 
 export type DScrollTab = {
@@ -69,11 +70,21 @@ export const tabList: Array<DScrollTab> = [
   },
 ]
 
+export type IAllStatus = {
+  allStatus: '1' | '2'
+  value: '企业话术' | '个人话术'
+}
+
+export const allStatusList: IAllStatus[] = [
+  { allStatus: '1', value: "企业话术" },
+  { allStatus: '2', value: "个人话术" },
+]
 export interface DEmit {
 	(e: 'handle-change-emit', n: ChangeEmitParams): void
 	(e: 'handle-search-emit', n: NonNullable<DFindParams<IArchiveRequest, 'title'>>): void
 	(e: 'handle-tab-emit', n: DScrollTab): void
   (e: 'handle-see-more', n: DetailParams): void
+  (e: 'handle-all-status', n: DetailParams ): void
 }
 
 export interface DDataBaseListAll {

+ 35 - 2
src/util/authStorage.ts

@@ -1,7 +1,40 @@
-export const getStorage = () => JSON.parse(window.localStorage.getItem('X-token')!)
+import CryptoJS from 'crypto-js'
+
+export const getStorage = () => JSON.parse(window.localStorage.getItem('userInfo')!).token
 
 export const setStorage = (token: string) => window.localStorage.setItem('X-token', token)
 
 export const removeStorage = () => window.localStorage.removeItem('X-token')
 
-export const clearStorage = () => window.localStorage.clear()
+export const clearStorage = () => window.localStorage.clear()
+
+const key = CryptoJS.enc.Utf8.parse("caimei--20240103");
+const iv = CryptoJS.enc.Utf8.parse('caimei--20240103');
+/* 加密方法 */
+export function myEncrypt(word: string) {
+	const srcs = CryptoJS.enc.Utf8.parse(word);
+	const encrypted = CryptoJS.AES.encrypt(srcs, key, {
+		iv,
+		mode: CryptoJS.mode.CBC,
+		padding: CryptoJS.pad.NoPadding
+	});
+	console.log(CryptoJS.enc.Base64.stringify(encrypted.ciphertext));
+}
+/* 解密方法 */
+export function myDecrypt(word: string[]) {
+	if (word) {
+		return word.map(e => {
+			if (e) {
+				const encryptedHexStr = CryptoJS.enc.Base64.parse(e);
+				const srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);
+				const decrypt = CryptoJS.AES.decrypt(srcs, key, {
+					iv,
+					mode: CryptoJS.mode.CBC,
+					padding: CryptoJS.pad.NoPadding
+				});
+				const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
+				return decryptedStr.toString()
+			} else return ''
+		})
+	} else return []
+}

+ 7 - 2
src/util/http.ts

@@ -7,7 +7,7 @@ import axios, {
 } from "axios";
 import { showLoadingToast, showFailToast } from 'vant'
 import type { IResultData } from '@/types/api/index'
-import { getStorage } from './authStorage';
+import { getStorage } from './authStorage'
 
 enum RequestEnums {
 	TIME_OUT = 10000,
@@ -51,12 +51,17 @@ class RequestHttp implements IRequestClass {
 		})
 
 		this.service.interceptors.response.use((response: AxiosResponse) => {
-			const { msg, code } = response.data
+			const { msg, code, error } = response.data
 			console.log(response.data)
 			if (response.status === 302) {
+				window.localStorage.clear()
 				window.location.href = `/login?redirect=${window.location.pathname}`
 				return Promise.reject(msg)
 			}
+			if (response.status === 404) {
+				showFailToast(error)
+				return Promise.reject(msg)
+			}
 			if (code !== RequestEnums.CODE) {
 				showFailToast(msg)
 				return Promise.reject(msg)

+ 107 - 0
src/views/Database/add.vue

@@ -0,0 +1,107 @@
+<template>
+  <div class="add">
+    <el-form
+      ref="formRef"
+      label-position="top"
+      label-width="100px"
+      :model="formLabelAlign"
+      style="max-width: 460px"
+      :rules="formRules"
+    >
+      <el-form-item label="标题:" prop="title">
+        <el-input v-model="formLabelAlign.title" placeholder="请输入标题" />
+      </el-form-item>
+      <el-form-item label="话术内容:" prop="content">
+        <el-input
+          type="textarea"
+          v-model="formLabelAlign.content"
+          placeholder="请输入话术内容"
+        />
+      </el-form-item>
+    </el-form>
+    <div class="btn">
+      <data-button
+        @handle-emit="onSubmit(formRef)"
+        backgroundColor="#FF5B00"
+        color="#FFFFFF"
+        label="保存"
+      />
+      <data-button
+        style="margin-top: 12px;"
+        @handle-emit="router.back()"
+        backgroundColor="#FFFFFF"
+        color="#666666"
+        label="取消"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { DForm } from "@/types/views/add.type";
+import { reactive, ref } from "vue";
+import type { FormInstance, FormRules } from "element-plus";
+import { useRouter } from "vue-router";
+import { useUserInfoState } from "@/store/user/user";
+import { addAllStatusForm } from "@/api/context/context";
+const router = useRouter();
+const { serviceProviderId } = useUserInfoState()
+const formRef = ref<FormInstance>();
+const formLabelAlign = reactive<DForm>({
+  title: "",
+  content: "",
+  type: "5",
+  spId: serviceProviderId as number
+});
+
+const formRules = reactive<FormRules<DForm>>({
+  title: [
+    {
+      required: true,
+      message: "请输入标题",
+    },
+  ],
+  content: [
+    {
+      required: true,
+      message: "请输入话术",
+    },
+  ],
+});
+
+const onSubmit = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  formEl.validate(async (valid, fields) => {
+    if (valid) {
+      const data = await addAllStatusForm(formLabelAlign)
+      if (data.code === 0) {
+        router.back()
+      }
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+</script>
+
+<style scoped lang="scss">
+.add {
+  padding: 30px 15px;
+}
+:deep() {
+  .el-input {
+    height: 45px;
+    font-size: $basicFS;
+  }
+  .el-textarea__inner {
+    height: 128px;
+  }
+  .el-form-item__label {
+    font-size: $basicFS;
+    color: #22272E;
+  }
+}
+.btn {
+  padding: 45px 15px;
+}
+</style>

+ 45 - 24
src/views/Database/components/context-contain.vue

@@ -1,14 +1,22 @@
-
 <template>
   <div class="box">
     <slot name="head" :data="props" />
-    <div v-for="(d,i) in dataList" :key="i"> 
+    <div v-for="(d, i) in dataList" :key="i">
       <div class="shareFile">
         <div class="time">{{ d.addTime }}</div>
         <slot name="share" :data="d" />
       </div>
-      <file-content :title="d.title" :type="d.type" :shop-name="d.shopName" />
+      <file-content
+        :title="d.title"
+        :type="d.type"
+        :shop-name="d.shopName"
+        :id="d.id"
+        :waters="d.waters"
+        :content="d.content"
+        :image="d.image"
+      />
     </div>
+    <div v-if="props.dataList.length === 0" class="empty">暂无任何文件~</div>
     <div class="foot-btn" v-if="dataList.length >= 5">
       <data-button
         backgroundColor="#fff"
@@ -22,31 +30,44 @@
 </template>
 
 <script setup lang="ts">
-import { DDataBaseListAll, DEmit } from '@/types/views/database.type';
+import { DDataBaseListAll, DEmit } from "@/types/views/database.type";
 import FileContent from "./file-content.vue";
-import { ref } from "vue";
+import { ref, computed } from 'vue';
 
-const pageNum = ref<number>(1)
-const emit = defineEmits<DEmit>()
+const pageNum = ref<number>(1);
+const emit = defineEmits<DEmit>();
 
 const props = defineProps<DDataBaseListAll & { showMore: boolean }>()
 
-const btnLabel = ref<string>("查看更多");
+const btnLabel = computed<string>(() => isSeeMore.value ? "折叠" : '查看更多');
+const isSeeMore = computed<boolean>(() => pageNum.value > 1 && props.dataList.length === 0)
 
-console.log(props.dataList.length)
+console.log("传递的props", props.dataList);
 
 const handleSeeMore = () => {
-  pageNum.value++
+  if (isSeeMore.value) {
+    pageNum.value = 1
+  } else {
+    pageNum.value++
+  }
   console.log(props.type, pageNum.value);
-  emit('handle-see-more', {
+  emit("handle-see-more", {
     type: props.type,
-    pageNum: pageNum.value
-  })
-  console.log('点击了查看更多')
-}
+    pageNum: pageNum.value,
+  });
+  console.log(`点击了${btnLabel.value}`);
+};
 </script>
 
 <style scoped lang="scss">
+.empty {
+  width: 100%;
+  height: 135px;
+  background: #fff;
+  @include display-flex-center;
+  font-size: $basicFS-foot;
+  color: #909090;
+}
 .box {
   background: #fff;
   width: 100%;
@@ -80,14 +101,14 @@ const handleSeeMore = () => {
   }
 }
 :deep() {
-	.van-popup--center {
-		.van-swipe__track,
-		.van-swipe-item {
-			width: 100% !important;
-		}
-		.van-image-preview__index {
-			display: none;
-		}
-	}
+  .van-popup--center {
+    .van-swipe__track,
+    .van-swipe-item {
+      width: 100% !important;
+    }
+    .van-image-preview__index {
+      display: none;
+    }
+  }
 }
 </style>

+ 37 - 18
src/views/Database/components/context-head.vue

@@ -1,12 +1,13 @@
 <template>
   <div>
-    <div class="title" v-if="storeType === '0'">{{ controlText }}</div>
+    <div class="title" v-if="route.query.t === '0'">{{ controlText }}</div>
     <div v-if="type === '5'" class="enterprise">
-      <van-tabs v-model:active="active" shrink>
+      <van-tabs v-model:active="form.allStatus" shrink>
         <van-tab
-          v-for="item in enterpriseOrIndividual"
+          v-for="item in allStatusList"
           :title="item.value"
-          :key="item.id"
+          :key="item.allStatus"
+          :name="item.allStatus"
         />
       </van-tabs>
     </div>
@@ -16,27 +17,45 @@
 <script setup lang="ts">
 import {
   ChangeTabEmit,
+  DEmit,
   DFindParams,
   DScrollTab,
   tabList,
+  allStatusList,
 } from "@/types/views/database.type";
-import { ref, computed, toRefs } from "vue";
-import { useTabStore } from '../../../store/tab/tab';
-
-const { type: storeType } = toRefs(useTabStore())
-
+import { ref, computed, toRefs, watch } from "vue";
+import { useTabStore } from "@/store/tab/tab";
+import { DTabStore } from "@/store/tab/tab.type";
+import { useRoute } from "vue-router";
+const { productId, labelIds, stageStatus, searchValue } = toRefs(useTabStore());
+const route = useRoute();
 const props = defineProps<{
   type: ChangeTabEmit;
 }>();
-
-console.log("插槽传递的值为:", props);
-
-const enterpriseOrIndividual = [
-  { id: 0, value: "企业话术" },
-  { id: 1, value: "个人话术" },
-];
-
-const active = ref<number>(0);
+const form = ref<DTabStore>({
+  labelIds: labelIds.value,
+  productId: productId.value,
+  stageStatus: stageStatus.value,
+  type: route.query.t as ChangeTabEmit,
+  searchValue: searchValue.value,
+  allStatus: "1",
+});
+const emit = defineEmits<DEmit>();
+watch(
+  () => route.query,
+  () => {
+    form.value.allStatus = '1'
+  },
+  {
+    immediate: true,
+  }
+);
+watch(
+  () => form.value.allStatus,
+  () => {
+    emit("handle-all-status", { type: props.type, allStatus: form.value.allStatus });
+  }
+);
 
 const controlText = computed<DFindParams<DScrollTab, "text">>(
   () => tabList.filter((e) => e.type === props.type)[0].text

+ 44 - 35
src/views/Database/components/context-share.vue

@@ -1,53 +1,62 @@
 <template>
-	<div class="share" v-if="type === '5'">
-		<div class="show" @click.stop="useCopyText(props.content!)">复制话术内容</div>
-		<div class="showNum" @click.stop="handleShare(props)">已使用:5次</div>
-	</div>
-	<div class="share" v-else>
-		<div class="show" @click.stop="handleDetail(props)">查看{{ controlText }}</div>
-		<div class="show" @click.stop="handleShare(props)">分享</div>
-	</div>
+  <div class="share" v-if="type === '5'">
+    <div class="show" @click.stop="useCopyText(props.content!)">复制话术内容</div>
+    <div class="showNum" v-if="allStatus === 1">已使用:{{ props.pv || 0 }}次</div>
+  </div>
+  <div class="share" v-else>
+    <div class="show" @click.stop="handleDetail(props)">查看{{ controlText }}</div>
+    <div
+      class="show"
+      @click.stop="useWeChatShare({ type: props.type!, id: props.id!, spId: serviceProviderId as number })"
+    >
+      分享
+    </div>
+  </div>
 </template>
 
 <script setup lang="ts">
-import { DArchiveResultData } from '@/types/api/context.type';
-import { DFindParams, DScrollTab, tabList, DetailParams } from '@/types/views/database.type';
-import { computed } from 'vue';
-import { useRouter } from 'vue-router';
-import useCopyText from '@/Hooks/useCopyText';
+import { DArchiveResultData } from "@/types/api/context.type";
+import { DFindParams, DScrollTab, tabList } from "@/types/views/database.type";
+import { computed } from "vue";
+import { useRouter } from "vue-router";
+import useCopyText from "@/Hooks/useCopyText";
+import useWeChatShare from "@/Hooks/useWeChatShare";
+import { useUserInfoState } from "@/store/user/user";
 
-const props = defineProps<DArchiveResultData>()
-
-const router = useRouter()
+const props = defineProps<DArchiveResultData>();
+const { serviceProviderId } = useUserInfoState();
+const router = useRouter();
 
 const controlText = computed<DFindParams<DScrollTab, "text">>(
   () => tabList.filter((e) => e.type === props.type)[0].text
 );
-
 const handleDetail = ($event: DArchiveResultData) => {
+  const link =
+    import.meta.env.VITE_HTTP_URL + (Number($event.type) === 7
+      ? "/encyclopedia/detail-"
+      : "/info/detail-") + $event.id + ".html";
+  if (Number($event.type) > 5) return window.open(link);
   router.push("/database/detail?id=" + $event.id + "&t=" + $event.type);
   console.log($event.type);
 };
-const handleShare = ($event: DArchiveResultData) => {}
-
 </script>
 
 <style scoped lang="scss">
 .share {
-	display: flex;
-	align-items: center;
-	.show {
-		margin-left: 24px;
-		color: $basicColor;
-		font-size: $basicFS;
-	}
-	.showNum {
-		margin-left: 24px;
-		background: #F5F5F5;
-		padding: 3px 9px;
-		border-radius: 2px;
-		font-size: $basicFS-foot;
-		color: #666;
-	}
+  display: flex;
+  align-items: center;
+  .show {
+    margin-left: 24px;
+    color: $basicColor;
+    font-size: $basicFS;
+  }
+  .showNum {
+    margin-left: 24px;
+    background: #f5f5f5;
+    padding: 3px 9px;
+    border-radius: 2px;
+    font-size: $basicFS-foot;
+    color: #666;
+  }
 }
-</style>
+</style>

+ 40 - 24
src/views/Database/components/file-content.vue

@@ -1,43 +1,59 @@
 <template>
   <div class="all-file">
     <div class="container">
-			<video-img-content v-if="showVideoImgContent()" :title="title!" :waters="waters" :type="type" :content="content" />
-			<text-file-content v-else :title="title" :type="type" :content="content" :waters="waters" />
-		</div>
-		<div class="foot">
-			<div class="mark">随便看看</div>
-			<div class="mark">{{ shopName }}</div>
-			<div class="mark">{{ keywords }}</div>
-		</div>
+      <video-img-content
+        v-if="showVideoImgContent()"
+        :title="title!"
+        :waters="waters"
+        :type="type"
+        :content="content"
+        :id="id"
+        :image="image"
+      />
+      <text-file-content
+        v-else
+        :title="title"
+        :type="type"
+        :content="content"
+        :waters="waters"
+        :id="id"
+				:image="image"
+      />
+    </div>
+    <div class="foot">
+      <div class="mark">随便看看</div>
+      <div class="mark">{{ shopName }}</div>
+      <div class="mark">{{ keywords }}</div>
+    </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import VideoImgContent from './video-img-content.vue'
-import TextFileContent from './text-file-content.vue';
-import { DArchiveResultData } from '@/types/api/context.type';
+import VideoImgContent from "./video-img-content.vue";
+import TextFileContent from "./text-file-content.vue";
+import { DArchiveResultData } from "@/types/api/context.type";
 
-const props = defineProps<DArchiveResultData>()
-
-const showVideoImgContent = (): boolean => ["1", "2", "4"].indexOf(props.type as string) !== -1
+const props = defineProps<DArchiveResultData>();
 
+const showVideoImgContent = (): boolean =>
+  ["1", "2", "4"].indexOf(props.type as string) !== -1;
 </script>
 
 <style lang="scss" scoped>
 .all-file {
-	width: 100%;
-	padding: 12px 0 9px 0;
-	margin-bottom: 14px;
-	border-bottom: 1px solid #E1E1E1;
+  width: 100%;
+  padding: 12px 0 9px 0;
+  margin-bottom: 14px;
+  border-bottom: 1px solid #e1e1e1;
 }
 .container {
-	margin-bottom: 19px;
+  margin-bottom: 19px;
 }
 .foot {
-	@include display-flex-between;
-	font-size: $basicFS-foot;
-	.mark {
-		color: $basicColor-foot;
-	}
+  @include display-flex-between;
+  font-size: $basicFS-foot;
+  .mark {
+    color: $basicColor-foot;
+  }
 }
 </style>

+ 18 - 10
src/views/Database/components/high-light-text.vue

@@ -1,16 +1,24 @@
 <template>
-	<van-highlight :keywords="props.keywords" :source-string="props.text" v-if="keywords" />
-	<div v-else>{{ props.text }}</div>
+  <van-highlight
+    :keywords="keywords"
+    :source-string="text"
+    v-if="keywords"
+    highlight-class="custom-class"
+  />
+  <div v-else>{{ text }}</div>
 </template>
 
 <script setup lang="ts">
-const props = defineProps<{
-	keywords: string
-	text: string 
-}>()
-
+defineProps<{
+  keywords: string;
+  text: string;
+}>();
 </script>
 
-<style scoped>
-
-</style>
+<style scoped lang="scss">
+:deep() {
+	.custom-class {
+		color: $basicColor;
+	}
+}
+</style>

+ 4 - 4
src/views/Database/components/scroll-tap-btn.vue

@@ -6,7 +6,7 @@
         :key="i.type"
         :label="i.text"
         @handle-emit="handleChangeTab(i)"
-        :class="sType === i.type ? 'active' : ''"
+        :class="route.query.t === i.type ? 'active' : ''"
         backgroundColor="#fff"
         color="#666666"
       />
@@ -15,14 +15,14 @@
 </template>
 
 <script setup lang="ts">
-import { toRefs } from 'vue';
 import { DScrollTab, DEmit, tabList } from '@/types/views/database.type';
-import { useTabStore } from '@/store/tab/tab';
+import { useRoute } from 'vue-router';
 
-const { type: sType } = toRefs(useTabStore())
+const route = useRoute()
 
 const emit = defineEmits<DEmit>()
 const handleChangeTab = (event: DScrollTab): void => {
+  console.log('点击tab菜单', event)
 	emit('handle-tab-emit', event)
 };
 

+ 95 - 75
src/views/Database/components/search-head.vue

@@ -1,20 +1,35 @@
 <template>
   <div class="search-head">
-    <van-search v-model="value" placeholder="请输入搜索关键词" @search="emit('handle-search-emit', value)" />
+    <van-search
+      v-model="value"
+      placeholder="请输入搜索关键词"
+      @search="emit('handle-search-emit', value)"
+    />
     <van-dropdown-menu active-color="#FF5B00">
       <van-dropdown-item
-			  active-color="#FF5B00"
+        active-color="#FF5B00"
         :title="i.title"
         ref="itemRef"
         v-for="(i, o) in menuTypeOptions"
         :key="o"
         v-model="i.changeValue"
         :options="i.type !== 'Double' ? i.options : []"
-				:class="[activeMenu === o ? 'activeMenu' : '']"
-				@close="handleDropdownClose"
+        @close="handleDropdownClose"
+        @open="handleOpen(o)"
       >
-        <template v-if="o !== 3">
-          <van-search :disabled="o === 2" v-model="searchValue" placeholder="请输入搜索关键词" />
+        <template v-if="o === 0">
+          <van-search
+            v-model="labelKeyParams.v"
+            placeholder="请输入搜索关键词"
+            @search="reqLabelKeyList"
+          />
+        </template>
+        <template v-if="o === 1">
+          <van-search
+            v-model="storeParams.productName"
+            placeholder="请输入搜索关键词"
+            @search="reqStoreList"
+          />
         </template>
         <template v-if="i.type === 'Double'">
           <van-checkbox-group v-model="checked">
@@ -25,7 +40,7 @@
                 :key="index"
                 :title="item.text"
                 @click="toggle(item.value)"
-								:class="checked.indexOf(item.value) > -1 ? 'd-checked' : ''"
+                :class="checked.indexOf(item.value) > -1 ? 'd-checked' : ''"
               >
                 <template #right-icon>
                   <van-checkbox
@@ -43,16 +58,28 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onBeforeUpdate, onMounted, VNodeRef } from 'vue';
-import type { DMenu, DMenuList, DEmit } from '@/types/views/database.type';
+import { ref, reactive, onBeforeUpdate, onMounted, VNodeRef } from "vue";
+import type { DMenu, DMenuList, DEmit } from "@/types/views/database.type";
 import { getLabelKeyList, getStoreList } from "@/api/context/context";
-import { CheckboxInstance } from 'vant';
+import { CheckboxInstance } from "vant";
+import { ILabelKeyRequest, IStoreRequest } from "@/types/api/context.type";
 
 type menuOptions = Array<DMenuList<DMenu>>; // 菜单类型
-
-const activeMenu = ref<number>(0) // 切换tab
-const searchValue = ref<string>('') // 搜索条件
-const value = ref<string>(''); // 标题搜索
+const activePadding = ref<string>("20vw 1vw 1vw 1vw"); // vue 样式
+const handleOpen = (val: number) => {
+  activePadding.value = val < 2 ? "20vw 1vw 1vw 1vw" : "5vw 1vw 1vw 1vw";
+};
+const storeParams = ref<IStoreRequest>({
+  pageNum: 1,
+  pageSize: 10,
+  productName: "",
+}); // 搜索条件
+const labelKeyParams = ref<ILabelKeyRequest>({
+  pageNum: 1,
+  pageSize: 10,
+  v: "",
+}); // 搜索条件
+const value = ref<string>(""); // 标题搜索
 const checked = ref<Array<number | null>>([]); // 默认选中
 const checkboxRefs = ref<Array<CheckboxInstance | VNodeRef>>([]); // 多选默认
 
@@ -98,44 +125,36 @@ const menuTypeOptions = reactive<menuOptions>([
 ]);
 
 const toggle = (index: number) => {
-	(checkboxRefs.value[index] as CheckboxInstance).toggle();
+  (checkboxRefs.value[index] as CheckboxInstance).toggle();
 };
-const emit = defineEmits<DEmit>()
+const emit = defineEmits<DEmit>();
 const handleDropdownClose = (): void => {
-	emit('handle-change-emit', {
+  emit("handle-change-emit", {
     labelIds: checked.value.join(),
     productId: menuTypeOptions[1].changeValue!,
-    stageStatus: menuTypeOptions[2].changeValue!.toString()
-  })
-}
+    stageStatus: menuTypeOptions[2].changeValue!.toString(),
+  });
+};
 
 const reqLabelKeyList = async () => {
-  const { data } = await getLabelKeyList({
-    pageNum: 1,
-    pageSize: 10,
-    v: ''
-  })
-  menuTypeOptions[0].options = data!.list!.map(e => ({
+  const { data } = await getLabelKeyList(labelKeyParams.value);
+  menuTypeOptions[0].options = data!.list!.map((e) => ({
     text: e.v,
-    value: Number(e.k)
-  }))
-}
+    value: Number(e.k),
+  }));
+};
 const reqStoreList = async () => {
-  const { data } = await getStoreList({
-    pageNum: 1,
-    pageSize: 10,
-    productName: ''
-  })
-  menuTypeOptions[1].options = data!.list!.map(e => ({
+  const { data } = await getStoreList(storeParams.value);
+  menuTypeOptions[1].options = [{ text: '商品(全部)',value: 0 }, ...data!.list!.map((e) => ({
     text: e.productName,
-    value: e.productId
-  }))
-}
+    value: e.productId,
+  }))]
+};
 
-onMounted(async () => {
-  await reqLabelKeyList()
-  await reqStoreList()
-})
+onMounted(() => {
+  reqLabelKeyList();
+  reqStoreList();
+});
 // 组件更新
 onBeforeUpdate(() => {
   checkboxRefs.value = [];
@@ -154,9 +173,9 @@ onBeforeUpdate(() => {
     font-size: 13px;
     padding: 0;
     border: 1px solid #e1e1e1;
-		.van-search__field {
-			padding: 0;
-		}
+    .van-search__field {
+      padding: 0;
+    }
     .van-search__content {
       background: #fff;
     }
@@ -182,31 +201,31 @@ onBeforeUpdate(() => {
     color: #333;
     position: relative;
   }
-	.van-cell {
-		padding: 7.5px;
-		.van-cell__title {
-			color: rgb(37, 1, 1);
-			font-size: 13px;
-		}
-	}
-	.van-checkbox__icon--checked .van-icon,
-	.van-checkbox__icon .van-icon {
-		background: none;
-		border: none;
-	}
+  .van-cell {
+    padding: 7.5px;
+    .van-cell__title {
+      color: rgb(37, 1, 1);
+      font-size: 13px;
+    }
+  }
+  .van-checkbox__icon--checked .van-icon,
+  .van-checkbox__icon .van-icon {
+    background: none;
+    border: none;
+  }
   .van-dropdown-menu__title::after {
     opacity: 1;
   }
-	.van-dropdown-item__option--active,
-	.van-dropdown-menu__title--active {
-		.van-cell__title,
-		.van-dropdown-menu__title::after {
-			color: $basicColor !important;
-		}
-	}
+  .van-dropdown-item__option--active,
+  .van-dropdown-menu__title--active {
+    .van-cell__title,
+    .van-dropdown-menu__title::after {
+      color: $basicColor !important;
+    }
+  }
 
   .van-dropdown-menu__title--active::before {
-    content: '';
+    content: "";
     position: absolute;
     width: 30px;
     height: 2px;
@@ -218,16 +237,17 @@ onBeforeUpdate(() => {
   .van-popup--top {
     max-height: 50vh;
     position: relative;
-    padding: 45px 5px 5px 5px;
+    padding: v-bind(activePadding);
     .van-search {
       position: absolute;
-      top: 24px;
+      top: 40px;
       left: 50%;
       width: 96%;
       height: 40px;
       transform: translate(-50%, -50%);
     }
-    .van-cell__title, .van-cell__value {
+    .van-cell__title,
+    .van-cell__value {
       flex: auto;
       width: 20px;
     }
@@ -237,14 +257,14 @@ onBeforeUpdate(() => {
   }
 }
 .d-checked {
-	:deep() {
-		.van-checkbox__icon--checked .van-icon,
-		.van-cell__title {
-			color: $basicColor;
-		}
-	}
+  :deep() {
+    .van-checkbox__icon--checked .van-icon,
+    .van-cell__title {
+      color: $basicColor;
+    }
+  }
 }
 .activeMenu {
-	color: $basicColor;
+  color: $basicColor;
 }
 </style>

+ 46 - 42
src/views/Database/components/text-file-content.vue

@@ -1,53 +1,57 @@
 <template>
-	<div class="file">
-		<div class="image" v-if="type !== '5'">
-			<van-image
-				fit="contain"
-				src="https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg"
-			/>
-		</div>
-		<div class="title">
-			<div class="t1"><high-light-text :keywords="'标题'" :text="title!" /></div>
-			<div class="t2" v-if="!showFileContent()"><high-light-text :keywords="'以色列'" :text="title!" /></div>
-		</div>
-	</div>
+  <div class="file">
+    <div class="image" v-if="type !== '5'">
+      <van-image fit="contain" :src="image" v-if="type === '6' || type === '7'" />
+      <van-image fit="contain" :src="getFileImg(props?.waters as string).img" v-else />
+    </div>
+    <div class="title">
+      <div class="t1"><high-light-text :keywords="searchValue" :text="title!" /></div>
+      <div class="t2" v-if="!showFileContent()">
+        <high-light-text :keywords="searchValue" :text="title!" />
+      </div>
+    </div>
+  </div>
 </template>
 
 <script lang="ts" setup>
-import highLightText from './high-light-text.vue';
-import { DArchiveResultData } from '@/types/api/context.type';
+import highLightText from "./high-light-text.vue";
+import { DArchiveResultData } from "@/types/api/context.type";
+import { useTabStore } from "@/store/tab/tab";
+import { toRefs } from "vue";
+import getFileImg from "@/Hooks/useFileImage";
 
-const props = defineProps<DArchiveResultData>()
+const { searchValue } = toRefs(useTabStore());
+const props = defineProps<DArchiveResultData>();
 
-const showFileContent = (): boolean => ["3", "6"].indexOf(props.type as string) !== -1
+const showFileContent = (): boolean => ["3", "6"].indexOf(props.type as string) !== -1;
 </script>
 
 <style lang="scss" scoped>
 .file {
-	display: flex;
-	align-items: center;
-	.image {
-		width: 63px;
-		height: 63px;
-		@include display-flex-center;
-		margin-right: 12px;
-	}
-	.title {
-		font-size: $basicFS;
-		min-width: calc(100% - 75px);
-		display: flex;
-		justify-content: space-around;
-		flex-direction: column;
-		height:63px;
-		.t1 {
-			color: $basicColor-content;
-			@include webkit-line-clamp (1);
-		}
-		.t2 {
-			line-height: 21px;
-			color: $basicColor-foot;
-			@include webkit-line-clamp (2);
-		}
-	}
+  display: flex;
+  align-items: center;
+  .image {
+    width: 63px;
+    height: 63px;
+    @include display-flex-center;
+    margin-right: 12px;
+  }
+  .title {
+    font-size: $basicFS;
+    width: calc(100% - 75px);
+    display: flex;
+    justify-content: space-around;
+    flex-direction: column;
+    height: 63px;
+    .t1 {
+      color: $basicColor-content;
+      @include webkit-line-clamp(1);
+    }
+    .t2 {
+      line-height: 21px;
+      color: $basicColor-foot;
+      @include webkit-line-clamp(2);
+    }
+  }
 }
-</style>
+</style>

+ 11 - 10
src/views/Database/components/video-img-content.vue

@@ -1,13 +1,11 @@
 <template>
   <div>
     <div class="text-title">
-      <high-light-text :keywords="''" :text="props.title!" />
+      <high-light-text :keywords="searchValue" :text="props.title!" />
     </div>
     <div class="img-content" v-if="type === '1'">
-      <div class="item">
+      <div class="item" v-for="(img, i) in waters" :key="i">
         <van-image
-          v-for="(img, i) in waters"
-          :key="i"
           fit="contain"
           :src="img"
           @click.native="shopImagePreviews(i)"
@@ -25,7 +23,7 @@
         x5-video-player-type="h5"
         controlslist="nodownload"
       >
-        <source src="https://caimei-oss.oss-cn-shenzhen.aliyuncs.com/beta/archiveFile/07be2bc1cf6d4b649d694c0c81edd50d.mp4?Expires=1703755722&OSSAccessKeyId=LTAI4GBL3o4YkWnbKYgf2Xia&Signature=FkraWtEi6HmZNYoKvMgoNVsNbmU%3D" type="video/mp4" />
+        <source :src="waters![0]" />
       </video>
     </div>
     <div class="text-content" v-if="type === '4'">
@@ -35,18 +33,21 @@
 </template>
 
 <script setup lang="ts">
+import { useTabStore } from "@/store/tab/tab";
 import highLightText from "./high-light-text.vue";
 import { DArchiveResultData } from "@/types/api/context.type";
 import { showImagePreview } from "vant";
+import { toRefs } from 'vue';
+const { searchValue } = toRefs(useTabStore())
 
-const props = withDefaults(defineProps<DArchiveResultData>(), {
-  waters: () => ["https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg"],
-});
+const props = defineProps<DArchiveResultData>()
+
+console.log('图片文件', props.waters)
 
 const shopImagePreviews = (index: number) => {
   console.log(index);
   showImagePreview({
-    images: props.waters!,
+    images: props.waters as string[],
     startPosition: index,
   });
 };
@@ -62,7 +63,7 @@ const shopImagePreviews = (index: number) => {
 }
 .img-content {
   display: grid;
-  grid-template-columns: repeat(3fr);
+  grid-template-columns: repeat(3, 1fr);
   grid-gap: 12px;
   .item {
     border: 1px solid #e1e1e1;

+ 97 - 39
src/views/Database/data-all/index.vue

@@ -4,83 +4,141 @@
       :type="data.type"
       :data-list="data.dataList"
       :show-more="showSeeMore"
-      v-if="data.dataList.length > 0 && data.type !== '0'"
+      v-if="data.dataList.length > 0 && data.type !== '0' && data.type !== '5'"
       @handle-see-more="handleSeeMore"
     >
       <template #head="{ data }">
-        <context-head :type="data.type" />
+        <context-head :type="data.type"/>
       </template>
       <template #share="{ data }">
         <context-share :type="data.type!" :id="data.id" :content="data.content" />
       </template>
     </context-contain>
-    <div v-if="isEmpty && i === 0" class="empty">
-      暂无任何文件~
-    </div>
+    <context-contain
+      :type="data.type"
+      :data-list="data.dataList"
+      :show-more="showSeeMore"
+      v-if="isShowType && data.type === '5'"
+      @handle-see-more="handleSeeMore"
+    >
+      <template #head="{ data }">
+        <context-head :type="data.type" @handle-all-status="handleAllStatus" />
+      </template>
+      <template #share="{ data }">
+        <context-share :type="data.type!" :id="data.id" :content="data.content" :all-status="data.allStatus" />
+      </template>
+    </context-contain>
+  </div>
+  <div v-if="isEmpty && route.query.t !== '5'" class="empty">暂无任何文件~</div>
+  <div class="bottom-btn" v-if="reqParams.type === '5' && isAddStatus">
+    <data-button
+      @handle-emit="router.push('/database/add')"
+      backgroundColor="#FF5B00"
+      color="#FFFFFF"
+      label="添加话术"
+    />
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref, computed, watchEffect, onMounted } from "vue";
+import { computed, ref, watch } from 'vue';
 import contextContain from "../components/context-contain.vue";
 import { DDataBaseListAll, DetailParams, tabList } from "@/types/views/database.type";
 import contextHead from "../components/context-head.vue";
 import contextShare from "../components/context-share.vue";
-import { useRoute } from "vue-router";
+import { useRouter, useRoute, LocationQueryValue } from "vue-router";
 import { getArchiveList } from "@/api/context/context";
-import { IArchiveRequest } from "@/types/api/context.type"
-
+import { IArchiveRequest } from "@/types/api/context.type";
+import { useUserInfoState } from "@/store/user/user";
+import { myDecrypt } from '@/util/authStorage';
+const { userId } = useUserInfoState();
+const router = useRouter();
 const route = useRoute();
 const dataList = ref<DDataBaseListAll[] | null>(null);
-const setNull = (t: any) => (t === "0" ? "" : t);
-const reqParams = computed<IArchiveRequest>(
-  () =>
-    ({
-      type: setNull(route.query.t),
-      title: route.query.sv,
-      productId: setNull(route.query.pId),
-      labelIds: route.query.lId,
-      stageStatus: setNull(route.query.ss),
-      allStatus: route.query.as,
-      pageNum: 1,
-    } as IArchiveRequest)
-);
-
-const isEmpty = ref<boolean>(false)
-const showSeeMore = ref<boolean>(true)
+const routeQuery = (str: LocationQueryValue | LocationQueryValue[]) => str === '0' ? '' : str
+const reqParams = computed<IArchiveRequest>(() => ({
+  type: routeQuery(route.query.t),
+  title: route.query.sv,
+  productId: routeQuery(route.query.pId),
+  labelIds: route.query.lId,
+  stageStatus: routeQuery(route.query.ss),
+  spId: '',
+  userId: userId,
+}) as IArchiveRequest);
+const isAddStatus = ref<boolean>(false);
+const isEmpty = ref<boolean>(false);
+const isShowType = ref<boolean>(false);
+const showSeeMore = ref<boolean>(true);
+const formParams = ref({ allStatus: '1' })
 const reqArchiveList = async (res?: DetailParams) => {
   if (!res) {
-    const { data } = await getArchiveList({ ...reqParams.value });
+    const { data } = await getArchiveList({ ...reqParams.value, ...formParams.value });
+    const DcreptoList = data?.map(e => ({
+      ...e,
+      waters: myDecrypt(e.waters! as string[])
+    }))
     dataList.value = tabList.map((e) => ({
       type: e.type,
-      dataList: data!.filter((i) => i.type === e.type),
+      dataList: DcreptoList!.filter((i) => i.type === e.type),
     }));
+    isEmpty.value = data?.length === 0;
   } else {
     const { data } = await getArchiveList({ ...reqParams.value, ...res! });
-    dataList.value![res.type]!.dataList = [...dataList.value![res.type]!.dataList, ...data!]
-    isEmpty.value = dataList.value![res.type]!.dataList.length === 0
-    showSeeMore.value = data?.length! < 5
+    const DcreptoList = data?.map(e => ({
+      ...e,
+      waters: myDecrypt(e.waters! as string[])
+    }))
+    if (res.allStatus || res.pageNum === 1) {
+      dataList.value![res.type]!.dataList = [];
+    }
+    dataList.value![res.type]!.dataList = [
+      ...dataList.value![res.type]!.dataList,
+      ...DcreptoList!,
+    ];
+    isEmpty.value = dataList.value![res.type]!.dataList.length === 0 && reqParams.value.type !== '';
+    showSeeMore.value = data?.length! < 5;
   }
 };
-
-const handleSeeMore = ($event: DetailParams) => {
-  reqArchiveList($event)
-}
-watchEffect(() => {
-  reqArchiveList();
-  console.log(reqParams.value);
+watch(() => route.query, (val) => {
+  console.log('路由变化', val)
+  isShowType.value = route.query.t === '0' || route.query.t === '5'
+  formParams.value.allStatus = '1'
+  reqArchiveList()
+},
+{
+  immediate: true,
 });
 
-onMounted(() => {});
+const handleSeeMore = ($event: DetailParams) => {
+  console.log('查看更多')
+  reqArchiveList($event);
+};
+const handleAllStatus = ($event: DetailParams) => {
+  console.log('改变成企业', $event)
+  formParams.value.allStatus = $event.allStatus!
+  reqArchiveList($event);
+  isAddStatus.value = $event.allStatus === "2";
+};
 </script>
 
 <style lang="scss" scoped>
+.bottom-btn {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  padding: 24px 28px;
+  background: #fff;
+  z-index: 99;
+  box-sizing: border-box;
+  width: 100%;
+}
 .empty {
   width: 100%;
   height: 135px;
   background: #fff;
-  @include display-column-end;
+  @include display-flex-center;
   font-size: $basicFS-foot;
   color: #909090;
+  padding-top: 67px;
 }
 </style>

+ 90 - 69
src/views/Database/detail.vue

@@ -1,104 +1,114 @@
 <template>
   <div class="detail">
-    <div class="other" v-if="type === '5'">个人话术<span>已使用:{{ form?.pv || 0 }}次</span></div>
+    <div class="other" v-if="type === '5'">
+      {{ '个人话术' }}<span>已使用:{{ form?.pv || 0 }}次</span>
+    </div>
     <div class="time">{{ form?.addTime }}</div>
     <div class="title">{{ form?.title }}</div>
     <div class="text" v-if="type === '5'">
       {{ form?.content }}
     </div>
     <div class="content" v-if="type !== '5'">
-			<div v-html="form?.content" v-if="type === '4'" />
-			<div class="img-content" v-if="type === '1'">
-				<div class="item">
-					<van-image
-						v-for="(img, i) in form?.waters"
-						:key="i"
-						fit="contain"
-						:src="img"
-						@click.native="shopImagePreviews(i)"
-					/>
-				</div>
-			</div>
-			<div class="video-tontent" v-if="type === '2'">
-				<video
-					id="myVideoRef"
-					ref="myVideoRef"
-					class="video-js vjs-default-skin"
-					controls
-					autoplay
-					x5-video-player-fullscreen="true"
-					x5-video-player-type="h5"
-					controlslist="nodownload"
-				>
-					<source src="https://caimei-oss.oss-cn-shenzhen.aliyuncs.com/beta/archiveFile/07be2bc1cf6d4b649d694c0c81edd50d.mp4?Expires=1703755722&OSSAccessKeyId=LTAI4GBL3o4YkWnbKYgf2Xia&Signature=FkraWtEi6HmZNYoKvMgoNVsNbmU%3D" type="video/mp4" />
-				</video>
-			</div>
-		</div>
+      <div v-html="form?.content" v-if="type === '4'" />
+      <div class="file-content" v-if="type === '3'">
+        <van-image :src="getFileImg(form?.waters as string).img" />
+      </div>
+      <div class="img-content" v-if="type === '1'">
+        <div class="item" v-for="(img, i) in form?.waters" :key="i">
+          <van-image
+            fit="contain"
+            width="100%"
+            height="100%"
+            :src="img"
+            @click.native="shopImagePreviews(i)"
+          />
+        </div>
+      </div>
+      <div class="video-tontent" v-if="type === '2'">
+        <video
+          id="myVideoRef"
+          ref="myVideoRef"
+          class="video-js vjs-default-skin"
+          controls
+          autoplay
+          x5-video-player-fullscreen="true"
+          x5-video-player-type="h5"
+          controlslist="nodownload"
+        >
+          <source :src="(form?.waters as string)" />
+        </video>
+      </div>
+    </div>
     <p class="foot-mark">随便看看</p>
     <p class="foot-mark">{{ form?.shopName }}</p>
     <p class="foot-mark">{{ form?.keywords }}</p>
     <div class="data-btn">
       <data-button
-        @handle-emit="handleClick(1)"
+        @handle-emit="handleClick"
         backgroundColor="#FFF"
         color="#FF5B00"
         label="查看文件"
-				v-if="type === '3'"
+        v-if="type === '3'"
       />
       <data-button
-        @handle-emit="handleClick(1)"
+        @handle-emit="useCopyText(form?.content!)"
         backgroundColor="#FFF"
         color="#FF5B00"
         label="复制文本"
-				v-if="type === '5'"
+        v-if="type === '5'"
       />
       <data-button
-        @handle-emit="handleClick(1)"
+        @handle-emit="useWeChatShare({ type: form?.type!, id: form?.id! })"
         backgroundColor="#FFF"
         color="#FF5B00"
         label="分享"
-				v-if="type !== '5'"
+        v-if="type !== '5'"
       />
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
+import useCopyText from "@/Hooks/useCopyText";
+import useWeChatShare from "@/Hooks/useWeChatShare";
 import { getArchiveFormDetails } from "@/api/context/context";
 import { DArchiveResultData } from "@/types/api/context.type";
 import { ChangeTabEmit } from "@/types/views/database.type";
 import { showImagePreview } from "vant";
 import { computed, onMounted, ref } from "vue";
-import { useRoute } from "vue-router";
+import { useRoute, useRouter } from "vue-router";
+import getFileImg from "@/Hooks/useFileImage";
+import { useUserInfoState } from "@/store/user/user";
+import { myDecrypt } from "@/util/authStorage";
 const route = useRoute();
-
-const form = ref<DArchiveResultData>()
-
-const handleClick = ($event: number) => {
-  console.log($event);
-};
-
+const router = useRouter();
+const { userId } = useUserInfoState();
+const form = ref<DArchiveResultData>();
 const id = computed<string>(() => String(route.query.id));
 const type = computed<ChangeTabEmit>(() => route.query.t as ChangeTabEmit);
+const handleClick = () => {
+  router.push("/preview?t=" + type.value + "&id=" + id.value + "&isSp=" + 1);
+};
 
 const getArchiveForm = async () => {
   const { data } = await getArchiveFormDetails({
     id: id.value,
-    userId: "",
+    userId: userId as string,
   });
-	form.value = data
-	console.log(data);
+  const DcreptoList = { ...data, ...{ waters: myDecrypt(data!.waters! as string[]) } };
+  form.value = DcreptoList;
+  console.log(data);
 };
 const shopImagePreviews = (index: number) => {
   console.log(index);
   showImagePreview({
-    images: form.value?.waters!,
+    images: form.value?.waters! as string[],
     startPosition: index,
   });
 };
 
 onMounted(() => {
-	getArchiveForm()
+  getArchiveForm();
 });
 </script>
 
@@ -138,27 +148,38 @@ onMounted(() => {
     line-height: 21px;
     margin-bottom: 24px;
   }
-	.content {
-		margin-bottom: 18px;
-		.img-content {
-			display: grid;
-			grid-template-columns: repeat(3fr);
-			grid-gap: 12px;
-			.item {
-				border: 1px solid #e1e1e1;
-				width: 106px;
-				height: 106px;
-			}
-		}
-		.video-tontent {
-			width: 100%;
-			height: 210px;
-			video {
-				height: 100%;
-				width: 100%;
-			}
-		}
-	}
+  .content {
+    margin-bottom: 18px;
+    .img-content {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      grid-gap: 12px;
+      .item {
+        border: 1px solid #e1e1e1;
+        width: 106px;
+        height: 106px;
+      }
+    }
+    .video-tontent {
+      width: 100%;
+      height: 210px;
+      video {
+        height: 100%;
+        width: 100%;
+      }
+    }
+    .file-content {
+      height: 128px;
+      width: 100%;
+      @include display-flex-center;
+      :deep() {
+        .van-image {
+          width: 86px;
+          height: 86px;
+        }
+      }
+    }
+  }
   .foot-mark {
     color: $basicColor-foot;
     font-size: $basicFS-foot;
@@ -173,7 +194,7 @@ onMounted(() => {
   .van-button {
     border: 1px solid $basicColor;
     border-radius: 1px;
-		margin-bottom: 12px;
+    margin-bottom: 12px;
   }
 }
 </style>

+ 22 - 24
src/views/Database/index.vue

@@ -17,47 +17,45 @@ import type {
   ChangeEmitParams,
   DScrollTab,
 } from "@/types/views/database.type";
-import useStatisticalTime from "@/Hooks/useStatisticalTime";
-import { onUnmounted, ref } from "vue";
+import { ref, toRefs } from "vue";
 import { useRouter } from "vue-router";
 import { DTabStore } from "@/store/tab/tab.type";
-const tabStore = useTabStore();
+const { type } = toRefs(useTabStore());
+const { updatedTabStore } = useTabStore();
 const router = useRouter();
-const params = ref<DTabStore>({
-  type: "0",
-  labelIds: "",
-  stageStatus: "",
-  productId: 0,
-  searchValue: "",
+
+const form = ref<DTabStore>({
+  labelIds: '',
+  productId: '',
+  stageStatus: '',
+  type: type.value,
+  searchValue: '',
+  allStatus: "1",
 });
+
 const routerLink = () => {
-  tabStore.updatedTabStore(params.value);
   router.push(
-    `/database/index?t=${tabStore.type}&lId=${tabStore.labelIds}&sv=${tabStore.searchValue}&pId=${tabStore.productId}&ss=${tabStore.stageStatus}`
+    `/database/index?t=${form.value.type || "0"}&lId=${form.value.labelIds || ""}&sv=${
+      form.value.searchValue || ""
+    }&pId=${form.value.productId || ""}&ss=${form.value.stageStatus || ""}`
   );
 };
 const handleSearch = (event: SearchEmitParams) => {
-  params.value.searchValue = event;
+  form.value = { ...form.value, ...{ searchValue: event } }
+  updatedTabStore(form.value);
   routerLink();
 };
 const handleChange = (event: ChangeEmitParams) => {
-  params.value = { ...params.value, ...event };
+  form.value = { ...form.value, ...event }
+  updatedTabStore(form.value);
   routerLink();
 };
 const handleTabEmit = (event: DScrollTab): void => {
-  params.value.type = event.type;
+  console.log('tab', event)
+  form.value = { ...form.value, ...event }
+  updatedTabStore(form.value);
   routerLink();
 };
-
-const statistical = useStatisticalTime((time) => {
-  const t = Math.floor(time / 1000);
-  console.log("时长为:", t);
-  localStorage.setItem("spentTime", t.toString());
-});
-
-onUnmounted(() => {
-  statistical();
-});
 </script>
 
 <style scoped lang="scss">

+ 4 - 3
src/views/Login/index.vue

@@ -38,7 +38,8 @@ const { login } = useUserInfoState();
 const form = ref<ILogin>({
   mobileOrEmail: "",
   password: "",
-  isUnion: 1
+  isUnion: 1,
+  unionId: 'ox_1SwSaz62JsEppFjhlecqhENdQ'
 });
 
 const disabled = computed<boolean>(() => (!form.value.mobileOrEmail || !form.value.password))
@@ -48,9 +49,9 @@ const onSubmit = async () => {
     const { data, code } = await login(form.value);
     console.log(data);
     if (code === 0) {
-      router.push('/database/index?t=0')
+      router.replace('/database/index?t=0')
     } else {
-      console.log(2);
+      console.log('登录错误')
     }
   }
 };

+ 78 - 9
src/views/Preview/index.vue

@@ -1,17 +1,86 @@
 <template>
-	<video-preview></video-preview>
-	<pdf-preview></pdf-preview>
-	<image-preview></image-preview>
+  <video-preview v-if="params.type === '2' && fileUrl" :url="fileUrl" :is-permi="isPermi"/>
+  <pdf-preview v-if="params.type === '3' && fileUrl" :url="fileUrl" :is-permi="isPermi"/>
+  <image-preview v-if="params.type === '1' && form?.waters" :urls="form?.waters" :is-permi="isPermi"/>
+  <permi-mode v-if="!query.isSp" />
 </template>
 
 <script lang="ts" setup>
-//import { usePermission } from '@/Hooks/usePermission/usePermission'
-//import { useRoute } from 'vue-router';
-//const { isPermi, PermiMode } = usePermission() // 权限hook
+import { usePermission } from "@/Hooks/usePermission/usePermission";
+import { previewData } from "@/api/context/context";
+import useStatisticalTime from "@/Hooks/useStatisticalTime";
+import { onMounted, computed, onUnmounted, ref } from "vue";
+import { useRoute } from "vue-router";
+import { ChangeTabEmit } from "@/types/views/database.type";
+import useWeChatShare, { shareParams } from "@/Hooks/useWeChatShare";
+import { IPreviewForm } from "@/types/api/context.type";
+import { myDecrypt } from "@/util/authStorage";
+const { query } = useRoute()
 
+const form = ref<IPreviewForm>();
 
-</script>
+const fileUrl = ref<string>()
+
+const getPreviewData = async () => {
+  const { data } = await previewData({
+    userId: (query.uid as string) || "0",
+    id: query.id as string,
+    type: query.t as ChangeTabEmit,
+  });
+  const DcreptoList = { ...data, ...{ waters: myDecrypt(data?.waters as string[]) } } as IPreviewForm
+  form.value = DcreptoList
+  fileUrl.value = form.value.waters[0].split('?')[0]
+  console.log(form.value.waters);
+};
+const { isPermi, PermiMode } = usePermission(form.value?.permission! as number); // 权限hook
+const params = computed(() => ({
+  type: query.t as ChangeTabEmit,
+  userId: query.uid as string,
+  id: query.id as string,
+  spId: query.spId as string,
+}));
 
-<style scoped>
+onMounted(() => {
+  getPreviewData();
+  useWeChatShare(params.value! as unknown as shareParams);
+});
+
+const statistical = useStatisticalTime((time) => {
+  const t = Math.floor(time / 1000);
+  console.log("时长为:", t, Math.floor(time / 1000));
+  localStorage.setItem("spentTime", t.toString());
+  const f = {
+    productArchiveId: params.value.id,
+    headUserId: params.value.spId,
+    accessClient: "0",
+    pagePath: location.href,
+    accessDuration: t * 1000,
+    pageType: "67",
+    pageLabel: "内容库预览",
+    userId: params.value.userId || "0",
+    productId: form.value?.productId || "0",
+    shopId: "" || "0",
+    behaviorType: "1",
+  };
+  fetch(
+    `https://core-b.caimei365.com/user/record/Statistics?accessClient=${f.accessClient}&userId=${f.userId}&pagePath=${f.pagePath}&productId=${f.productId}&accessDuration=${f.accessDuration}&pageLabel=${f.pageLabel}&behaviorType=${f.behaviorType}&shopId=${f.shopId}`,
+    {
+      method: "GET",
+      headers: {
+        "Content-Type": "application/x-www-form-urlencoded"
+      },
+      keepalive: true,
+    }
+  )
+    .then((res) => res.json())
+    .then((data) => {
+      console.log('时间推送记录成功')
+      console.log(data);
+    })
+});
+onUnmounted(() => {
+  statistical()
+})
+</script>
 
-</style>
+<style scoped></style>

+ 10 - 3
src/views/error/404.vue

@@ -1,6 +1,6 @@
 <template>
-	<div>
-		404not fount
+	<div class="noFound">
+		请在手机端使用微信浏览器打开
 	</div>
 </template>
 
@@ -9,5 +9,12 @@
 </script>
 
 <style scoped>
-
+.noFound {
+	width: 100vw;
+	height: 100vh;
+	box-sizing: border-box;
+	padding-top: 20vh;
+	display: flex;
+	justify-content: center;
+}
 </style>