コミットを比較

...

66 コミット
master ... dev

作成者 SHA1 メッセージ 日付
  chutingting 1940c0b685 Merge branch 'ctt_dev_new' into dev 1年前
  chutingting 73400e88e6 edit 1年前
  chutingting c496128a43 Merge branch 'ctt_dev_new' into dev 1年前
  chutingting 11c06b5258 edit img 1年前
  chutingting d86470ba40 Merge branch 'ctt_dev_new' into dev 1年前
  chutingting bb406fafdf edit all 1年前
  chutingting ac9868188d Merge branch 'ctt_dev_new' into dev 1年前
  chutingting 6cc4be4260 edit bug 1年前
  chutingting 1ec8bf11af del <<< 1年前
  chutingting 35a7d20216 lang 1年前
  chutingting 17064ca715 Merge branch 'ctt_dev_new' into dev 1年前
  chutingting f2086edbae 中英文 1年前
  chutingting cb7beed615 Merge branch 'ctt_dev_new' into dev 1年前
  chutingting d322aad7fe 中英文 1年前
  chutingting 2a275bee1f Merge branch 'ctt_dev_new' into dev 1年前
  chutingting ae4c2c001c edit bug 1年前
  chutingting 9cba811432 Merge branch 'ctt_dev_new' into dev 1年前
  chutingting 4af1793c35 edit bug 1年前
  chutingting 80bbe4ad20 关联模版 1年前
  chutingting f66338a09b Merge branch 'ctt_dev_new' into dev 1年前
  chutingting a54db920c9 新增用户列表 1年前
  chutingting 2a7e33c08b Merge branch 'ctt_dev_new' into dev 1年前
  chutingting c9e452ec73 edit time 1年前
  chutingting 049d29513b Merge branch 'ctt_dev_new' into dev 1年前
  chutingting 1247c83be3 m模版 1年前
  chutingting e7b1b1dd67 Merge branch 'ctt_dev_new' into dev 1年前
  chutingting 1ea2a703fc 查看密钥 1年前
  chutingting a7bbbbde67 Merge branch 'ctt_dev_new' into dev 1年前
  chutingting 19d23fd81d 关联模版 1年前
  chutingting 118fe07754 Merge branch 'ctt_dev_new' into dev 1年前
  chutingting 5ec876f0d0 edit bug 1年前
  chutingting fbd76de8c5 Merge branch 'dev' of https://git.malls.iformall.com/suimang/Metavatar_Neuver_Admin into dev 1年前
  chutingting 355081afc9 edit bug 1年前
  chutingting c6a78b61bb Merge branch 'ctt_dev_new' of https://git.malls.iformall.com/suimang/Metavatar_Neuver_Admin into ctt_dev_new 1年前
  chutingting 8ad90f7ff1 Merge branch 'ctt_dev_new' into dev 1年前
  chutingting 0cf09fa32d edit id 1年前
  chutingting 63dd0f17b1 私有化 1年前
  chutingting d8021b8534 edit 1年前
  chutingting fcce2ac320 格式化数据 1年前
  chutingting 3a6cc2f7f6 视频 1年前
  chutingting 10c02576d9 区分权限 1年前
  chutingting 8f535ec278 个人信息 1年前
  chutingting cc4de57991 api管理 1年前
  HolyKnightIX 0729502903 upload 1年前
  HolyKnightIX c5724c8f3a upload 1年前
  HolyKnightIX c1c120f295 upload 1年前
  HolyKnightIX 0fa58b0e29 1 1年前
  HolyKnightIX fa32adcc87 upload 1年前
  HolyKnightIX c26b37a10d Merge branch 'dev_YWQ' into dev 1年前
  HolyKnightIX 9aaf01c726 add 1年前
  HolyKnightIX 5fde1e9028 Merge branch 'dev_YWQ' into dev 1年前
  HolyKnightIX 9a7a2915fc upload 1年前
  congzc fedcf416d9 1 1年前
  congzc a9617aecf0 1 1年前
  congzc 18fff628ca Merge branch 'dev' of https://git.malls.iformall.com/suimang/Metavatar_Neuver_Admin into dev 1年前
  congzc caf94c1922 Merge remote-tracking branch 'origin/dev_czc' into dev 1年前
  congzc 16a2b1a3fa 1 1年前
  HolyKnightIX a7ebf36f25 upload 1年前
  HolyKnightIX aac111f7ff 1 1年前
  HolyKnightIX 2cc33377a5 upload 1年前
  HolyKnightIX fe636bfa31 add 1年前
  congzc 850a644381 Merge branch 'dev' of https://git.malls.iformall.com/suimang/Metavatar_Neuver_Admin into dev 1年前
  congzc 57d44bc461 1 1年前
  HolyKnightIX 761197b8d2 upload 1年前
  HolyKnightIX c07bcb67c9 upload 1年前
  HolyKnightIX bdd6f53bfe upload 1年前
65個のファイルの変更8560行の追加3239行の削除
分割表示
  1. +4
    -2
      .env.development
  2. +1
    -1
      .env.production
  3. +1
    -1
      .env.test
  4. +1
    -1
      README.md
  5. +1
    -1
      index.html
  6. +1
    -1
      package.json
  7. +52
    -0
      src/api_mangage/business.js
  8. +39
    -0
      src/api_mangage/key.js
  9. +13
    -0
      src/apis/Permission.js
  10. +113
    -0
      src/apis/apiManage.js
  11. +14
    -0
      src/apis/editPass.js
  12. +83
    -0
      src/apis/home.js
  13. +11
    -11
      src/apis/login.js
  14. +1
    -1
      src/apis/myAccount.js
  15. +13
    -0
      src/apis/secretKey.js
  16. +13
    -0
      src/apis/system/userList.js
  17. +14
    -0
      src/apis/template.js
  18. +159
    -0
      src/apis/userManage.js
  19. +198
    -7
      src/lang/package/en.ts
  20. +197
    -6
      src/lang/package/zh-cn.ts
  21. +8
    -0
      src/layout/components/AppMain.vue
  22. +0
    -119
      src/layout/index copy 2.vue
  23. +0
    -131
      src/layout/index copy.vue
  24. +126
    -30
      src/layout/index.vue
  25. +1
    -1
      src/main.ts
  26. +25
    -24
      src/permission.ts
  27. +207
    -36
      src/router/index.ts
  28. +7
    -0
      src/store/modules/userInfo.js
  29. +23
    -4
      src/types/components.d.ts
  30. +0
    -67
      src/utils/request copy.js
  31. +6
    -6
      src/utils/request.js
  32. +5
    -1
      src/utils/request.ts
  33. +304
    -0
      src/views/apiManage/businessCheck.vue
  34. +519
    -0
      src/views/apiManage/index.vue
  35. +88
    -0
      src/views/apiManage/model.vue
  36. +301
    -0
      src/views/business/businessCheck.vue
  37. +351
    -0
      src/views/business/businessList.vue
  38. +2
    -2
      src/views/createVideo/index.vue
  39. +325
    -0
      src/views/dict/components/dict-item.vue
  40. +322
    -0
      src/views/dict/index.vue
  41. +116
    -0
      src/views/editPass/index.vue
  42. +104
    -0
      src/views/home/index.vue
  43. +301
    -0
      src/views/keys/keyCheck.vue
  44. +149
    -0
      src/views/keys/keyList.vue
  45. +75
    -289
      src/views/login/index.vue
  46. +16
    -0
      src/views/mangage/model.vue
  47. +16
    -0
      src/views/mangage/ruleList.vue
  48. +16
    -0
      src/views/mangage/userList.vue
  49. +0
    -698
      src/views/myCreating/index copy.vue
  50. +378
    -0
      src/views/privatization/index.vue
  51. +43
    -0
      src/views/secretKey/index.vue
  52. +124
    -0
      src/views/template/index.vue
  53. +91
    -0
      src/views/userManage/personMould.vue
  54. +146
    -0
      src/views/userManage/personVoice.vue
  55. +207
    -0
      src/views/userManage/rechargeList.vue
  56. +264
    -0
      src/views/userManage/userList.vue
  57. +180
    -0
      src/views/userManage/userMouldList.vue
  58. +188
    -0
      src/views/userManage/userVoiceList.vue
  59. +342
    -0
      src/views/userManage/videoList.vue
  60. +87
    -0
      src/views/userManage/voiceMould.vue
  61. +175
    -0
      src/views/video/index.vue
  62. +16
    -11
      vite.config.ts
  63. +123
    -0
      vite.config.ts.timestamp-1698900171336-585e6054472a3.mjs
  64. +235
    -164
      yarn-error.log
  65. +1619
    -1624
      yarn.lock

+ 4
- 2
.env.development ファイルの表示

@@ -5,6 +5,8 @@ NODE_ENV='development'

VITE_APP_TITLE = 'Metavatar-PC'
VITE_APP_PORT = 3000
## VITE_APP_BASE_API = 'https://photo.metavatar.cc/C'
VITE_APP_BASE_API = 'https://test.metavatar.cc/C'
## VITE_APP_BASE_API = 'https://photo.metavatar.cc/C' alitest 123456
VITE_APP_BASE_API = 'https://mtest.metavatar.cc/A'




+ 1
- 1
.env.production ファイルの表示

@@ -5,4 +5,4 @@ NODE_ENV='production'

VITE_APP_TITLE = 'Metavatar-PC'
VITE_APP_PORT = 3000
VITE_APP_BASE_API = 'https://neuver.metavatar.cc/C'
VITE_APP_BASE_API = 'https://neuver.metavatar.cc/A'

+ 1
- 1
.env.test ファイルの表示

@@ -6,4 +6,4 @@ NODE_ENV='test'
VITE_APP_TITLE = 'Metavatar-PC'
VITE_APP_PORT = 3000
# VITE_APP_BASE_API = 'https://photo.metavatar.cc/C'
VITE_APP_BASE_API = 'https://test.metavatar.cc/C'
VITE_APP_BASE_API = 'https://mtest.metavatar.cc/A'

+ 1
- 1
README.md ファイルの表示

@@ -1,4 +1,4 @@
<!-- 照片说话 慧影 -->
<!-- 邃芒A端-->
git commit -m "feat: "
git commit --no-verify -m "提交时的注释" 跳过检验
git merge origin/dev_

+ 1
- 1
index.html ファイルの表示

@@ -5,7 +5,7 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Metavatar-慧影</title>
<title>Metavatar-邃芒管理端</title>
</head>

<body>


+ 1
- 1
package.json ファイルの表示

@@ -24,6 +24,7 @@
"echarts": "^5.2.2",
"element-plus": "^2.3.1",
"js-audio-recorder": "^1.0.7",
"js-cookie": "^3.0.5",
"nprogress": "^0.2.0",
"path-browserify": "^1.0.1",
"path-to-regexp": "^6.2.0",
@@ -67,7 +68,6 @@
"stylelint-config-recommended-vue": "^1.4.0",
"stylelint-config-standard": "^32.0.0",
"stylelint-config-standard-scss": "^8.0.0",
"typescript": "^4.9.3",
"unocss": "^0.50.1",
"unplugin-auto-import": "^0.13.0",
"unplugin-icons": "^0.15.1",


+ 52
- 0
src/api_mangage/business.js ファイルの表示

@@ -0,0 +1,52 @@
import request from "@/utils/request.js";


/**
* @description 获取合作商列表
* @param {*} (pageNum,pageSize)
* @returns data
*/
export function businessListApi(pageNum, pageSize) {
return request({
url: `/serviceInfo/page?pageNum=${pageNum}&pageSize=${pageSize}`,
method: 'get'
})
}

/**
* @description 通过id获取合作商
* @param {*} id
* @returns data
*/
export function getBusinessByIdApi(id) {
return request({
url: `/serviceInfo/get?id=${id}`,
method: 'get'
})
}

/**
* @description 新增合作商
* @param {*} data
* @returns data
*/
export function addBusinessApi(data) {
return request({
url: `/serviceInfo/save`,
method: 'post',
data
})
}

/**
* @description 修改合作商
* @param {*} data
* @returns data
*/
export function updateBusinessApi(data) {
return request({
url: `/serviceInfo/update`,
method: 'post',
data
})
}

+ 39
- 0
src/api_mangage/key.js ファイルの表示

@@ -0,0 +1,39 @@
import request from "@/utils/request.js";


/**
* @description 获取密钥列表
* @param {*} (pageNum,pageSize)
* @returns data
*/
export function keyListApi(pageNum, pageSize) {
return request({
url: `/thirdPartyApi/page?pageNum=${pageNum}&pageSize=${pageSize}`,
method: 'get'
})
}

/**
* @description 根据id获取密钥详情
* @param {*} (id)
* @returns data
*/
export function getKeyByIdApi(id) {
return request({
url: `/thirdPartyApi/getByServiceId?serviceId=${id}`,
method: 'get'
})
}

/**
* @description 更改密钥状态
* @param {*} (id,status)
* @returns data
*/
export function updateKeyStatusApi(id, status) {
return request({
url: `/thirdPartyApi/updateStatus`,
method: 'post',
data: { id, status }
})
}

+ 13
- 0
src/apis/Permission.js ファイルの表示

@@ -0,0 +1,13 @@
import request from "@/utils/request.js";

/**
* @description:全查询导航菜单
* @param
* @return:
*/
export function getMenuNavApi() {
return request({
url: `/menu/nav`,
method: 'get',
})
}

+ 113
- 0
src/apis/apiManage.js ファイルの表示

@@ -0,0 +1,113 @@
import request from "@/utils/request.js";

/**
* @description 获取合作商列表
* @param {*} (pageNum,pageSize)
* @returns data
*/
export function businessListApi(pageNum, pageSize, type, id) {
return request({
url: `/serviceInfo/page?pageNum=${pageNum}&pageSize=${pageSize}&type=${type}&id=${id || ''}`,
method: 'get'
})
}

/**
* @description 新增合作商
* @param {*} data
* @returns data
*/
export function addBusinessApi(data) {
return request({
url: `/serviceInfo/save`,
method: 'post',
data
})
}

/**
* @description 获取api接入管理列表
* @param {*} (pageNum,pageSize)
* @returns data
*/
export function apiManageListApi(pageNum, pageSize) {
return request({
url: `/serviceInfo/page?type=1pageNum=${pageNum}&pageSize=${pageSize}`,
method: 'get'
})
}

/**
* @description 设置登录账号
* @param {*} data
* @returns data
*/
export function saveMallUserInfo(data) {
return request({
url: `/serviceInfo/saveMallUserInfo`,
method: 'post',
data
})
}

/**
* @description 修改登录账号 详情
* @param {*} data
* @returns data
*/
export function getMallUserInfo(id) {
return request({
url: `/serviceInfo/getMallUserInfo?serviceId=${id}`,
method: 'get'
})
}

/**
* @description 设置时长
* @param {*} data
* @returns data
*/
export function setRemainingTimes(data) {
return request({
url: `/serviceInfo/setRemainingTimes`,
method: 'post',
data
})
}

/**
* @description 所有模版
* @param {*} (pageNum,pageSize)
* @returns data
*/
export function modelAllList(pageNum, pageSize) {
return request({
url: `/personMould/alList?pageNum=${pageNum}&pageSize=${pageSize}`,
method: 'get'
})
}

/**
* @description 已关联模版
* @param {*}
* @returns data
*/
export function personMouldIdList(id) {
return request({
url: `/serviceInfo/personMouldIdList?serviceId=${id || ''}`,
method: 'get'
})
}

/**
* @description 保存关联模版
* @param {*}
* @returns data
*/
export function associatedMould(data) {
return request({
url: `/serviceInfo/associatedMould`,
method: 'post',
data
})
}

+ 14
- 0
src/apis/editPass.js ファイルの表示

@@ -0,0 +1,14 @@
import request from "@/utils/request.js";

/**
* @description 修改密码
* @params userName, pwd
* @returns data
*/
export function updatepwd(data) {
return request({
url: `/user/updatepwd`,
method: 'post',
data
})
}

+ 83
- 0
src/apis/home.js ファイルの表示

@@ -0,0 +1,83 @@
/*
* @Autor: Chutingting
* @Date: 2023-11-11 19:15:01
* @LastEditors: Chutingting
* @LastEditTime: 2023-11-13 10:28:20
* @Description:
*/
import request from "@/utils/request.js";
/**
* @description:用户角色
* @param
* @return:
*/
export function getUser() {
return request({
url: `/user/getUser`,
method: 'get',
})
}

/**
* @description:用户角色
* @param
* @return:
*/
export function localDeployApi() {
return request({
url: `/localDeploy`,
method: 'get',
})
}


/**
* @description: api 剩余时长
* @param
* @return:
*/
export function current() {
return request({
url: `/serviceInfo/current`,
method: 'get',
})
}

/**
* @description: 私有化 剩余时长
* @param
* @return:
*/
export function privateDeployAdminCurrent() {
return request({
url: `/serviceInfo/privateDeployAdminCurrent`,
method: 'get',
})
}

/**
* @description: 视频制作统计
* @param
* @return:
*/
export function videoListApi(pageNum, pageSize) {
let api = localStorage.getItem("isAdmin") === '0' ? '/serviceInfo/currentVideoRecords' : '/serviceInfo/privateDeployAdminCurrentVideoRecords'
return request({
url: `${api}?pageNum=${pageNum}&pageSize=${pageSize}`,
method: 'get',
})
}

/**
* @description: 已消耗总时长
* @param
* @return:
*/
export function currentVideoToalApi(pageNum) {
let api = localStorage.getItem("isAdmin") === '0' ? '/serviceInfo/currentVideoToal' : '/serviceInfo/privateDeployAdminCurrentVideoTotal'
return request({
url: `${api}`,
method: 'get',
})
}


+ 11
- 11
src/apis/login.js ファイルの表示

@@ -2,31 +2,31 @@ import request from "@/utils/request.js";

const baseURL = import.meta.env.VITE_APP_BASE_API


/**
* @description 注册
* @params email,password
* @description 登录
* @params email,password,code
* @returns data
*/
export function register(data) {
export function loginApi(data) {
return request({
// url: `/api/user/doRegisterByEmail`,
url: `/api/user/doRegisterByPhone`,
// url: `/api/user/loginByEmail`,
url: `/doLogin`,
method: 'post',
data
})
}

/**
* @description 登录
* @description 退出登录
* @params email,password,code
* @returns data
*/
export function login(data) {
export function logoutApi(data) {
return request({
// url: `/api/user/loginByEmail`,
url: `/api/user/login`,
method: 'post',
data
url: `/logout`,
method: 'get'
})
}

@@ -175,5 +175,5 @@ export function resetPwdFormApi(data) {
* @description:获取图形验证码
* @return: 验证码地址
*/
export const getCodeImgUrl = baseURL + '/api/user/captcha.jpg'
export const getCodeImgUrl = baseURL + '/captcha.jpg'


+ 1
- 1
src/apis/myAccount.js ファイルの表示

@@ -70,7 +70,7 @@ export function updUserInfoApi(data) {
*/
export function getUserInfoApi() {
return request({
url: `api/user/getUserInfo`,
url: `getUserInfo`,
method: 'get',
})
}

+ 13
- 0
src/apis/secretKey.js ファイルの表示

@@ -0,0 +1,13 @@

import request from "@/utils/request.js";
/**
* @description: 密钥查看
* @param
* @return:
*/
export function secretKeyApi() {
return request({
url: `/thirdPartyApi/current`,
method: 'get',
})
}

+ 13
- 0
src/apis/system/userList.js ファイルの表示

@@ -0,0 +1,13 @@
import request from "@/utils/request.js";

/**
* @description 获取用户列表
* @param sex ,pageNum,pageSize
* @returns data
*/
export function modeListApi(sex, pageNum, pageSize, videoType) {
return request({
url: `/api/personPatch/list?sex=${sex}&pageNum=${pageNum}&pageSize=${pageSize}&videoType=${videoType}`,
method: 'get'
})
}

+ 14
- 0
src/apis/template.js ファイルの表示

@@ -0,0 +1,14 @@
import request from "@/utils/request.js";

/**
* @description 获取合作商列表
* @param {*} (pageNum,pageSize)
* @returns data
*/
export function templateListApi(pageNum, pageSize) {
let api = localStorage.getItem("isAdmin") === '1' && localStorage.getItem("localDeploy") === 'true' ? '/personMould/alList' : '/personMould/serviceMouldList'
return request({
url: `${api}?pageNum=${pageNum}&pageSize=${pageSize}`,
method: 'get'
})
}

+ 159
- 0
src/apis/userManage.js ファイルの表示

@@ -0,0 +1,159 @@
import request from "@/utils/request.js";

/**
* @description 用户列表
* @param {*} (pageNum,pageSize)
* @returns data
*/
export function userListApi(params) {
return request({
url: `/wxCUserBasicInfo/list`,
method: 'get',
params
})
}
/**
* @description 用户列表-状态
* @param {*}
* @returns
*/
export function updateStatusApi(data) {
return request({
url: `/wxCUserBasicInfo/updateStatus`,
method: 'post',
data
})
}
// 绑定定制模板
export function personMouldListApi(pageNum, pageSize) {
return request({
url: `/personMould/alList?onlyCustomizedQuery=1&pageNum=${pageNum}&pageSize=${pageSize}`,
method: 'get'
})
}
// 绑定定制模板-已关联模版
export function userPersonMouldIdListApi(cUserId) {
return request({
url: `/personMould/userPersonMouldIdList?cUserId=${cUserId}`,
method: 'get'
})
}

// 绑定定制模板-关联模版
export function associatedUserMouldsApi(data) {
return request({
url: `/personMould/associatedUserMoulds`,
method: 'post',
data
})
}
// 绑定定制声纹
export function voiceMouldListApi(pageNum, pageSize) {
return request({
url: `/voiceMould/alList?onlyCustomizedQuery=1&pageNum=${pageNum}&pageSize=${pageSize}`,
method: 'get'
})
}
// 绑定定制声纹-已关联模版
export function userVoiceIdListApi(cUserId) {
return request({
url: `/voiceMould/userVoiceIdList?cUserId=${cUserId}`,
method: 'get'
})
}

// 绑定定制声纹-关联模版
export function associatedUserVoicesApi(data) {
return request({
url: `/voiceMould/associatedUserVoices`,
method: 'post',
data
})
}

/**
* @description 用户列表查看
* @param {*}
* @returns
*/
export function userListFindByIdApi(id) {
return request({
url: `/wxCUserBasicInfo/findById?id=${id}`,
method: 'get'
})
}

/**
* @description 私人定制模板列表
* @param {*} (pageNum,pageSize)
* @returns data
*/
export function userMouldListApi(params) {
return request({
url: `/personMould/userMouldList`,
method: 'get',
params
})
}
/**
* @description 私人定制声纹列表
* @param {*} (pageNum,pageSize)
* @returns data
*/
export function userVoiceListAPi(params) {
return request({
url: `/voiceMould/userVoiceList`,
method: 'get',
params
})
}

/**
* @description 充值列表
* @param {*} (pageNum,pageSize)
* @returns data
*/
export function rechargeListApi(params) {
return request({
url: `/productOrder/paiedOrders`,
method: 'get',
params
})
}

/**
* @description 视频列表
* @param {*} (pageNum,pageSize)
* @returns data
*/
export function videoListApi(params) {
return request({
url: `/userVideo/list`,
method: 'get',
params
})
}

/**
* @description 视频列表-查看
* @param {*}
* @returns
*/
export function videoListFindByIdApi(id) {
return request({
url: `/userVideo/findById?id=${id}`,
method: 'get'
})
}

/**
* @description 视频列表-删除
* @param {*}
* @returns
*/
export function videoListDelApi(id) {
return request({
url: `/userVideo/del?id=${id}`,
method: 'get'
})
}

+ 198
- 7
src/lang/package/en.ts ファイルの表示

@@ -15,7 +15,7 @@ export default {
// 登录页面国际化
login: {
// title: "Metavatar-MetaTalker",
title: "MetaTalker",
title: "MetaTalker-Admin",
username: "Username",
email: "Phone",
password: "Password",
@@ -24,7 +24,7 @@ export default {
register: "Sign up",
loginBtn: "Sign in",
registerBtn: "Sign up",
verifyCode: "Verify code",
captcha: "Verify code",
signIn: "Already got an account? Sign in here",
signUp: "Don't have an account? Sign up here",
forgetPwd: "I forgot my password",
@@ -51,6 +51,18 @@ export default {
logout: "Logout",
goLogin: "Go and log in",
LanguageChange: "Language switching",
home: 'Home',
apiManage: 'API Access Party Management',
privatization: 'Privatized customer management',
template: 'Template management',
secretKey: 'Key View',
video: 'Video production statistics',
userMange: 'User Management',
userList: 'User List',
userMouldList: 'Private Custom Template List',
userVoiceList: 'Private customized voiceprint list',
rechargeList: 'Recharge List',
videoList: 'Video List'
},

// 创作视频
@@ -203,10 +215,6 @@ export default {
comingSoon: "Coming Soon",
},

apiMangage: {
title: "API Mangage",
},

signSucceed: {
title: "Confirm your email",
succeed: "Sign Succeed !",
@@ -221,6 +229,189 @@ export default {
},

modelMangage: {
title: "Model Set",
title: "Model",
},

businessMangage: {
title: "Business",
name: "Owner",
address: "Address",
status: "Status",
code: "Code",
addNew: "Add new business",
createTime: "CreateTime",
cancel: "Cancel",
confirm: "Confirm",
nameInputText: "Please type owner",
addressInputText: "Please type address",
codeInputText: "Please type code and make sure it's only",
accessMethod: "Access Method",
typeInputText: "Please select your access method",
},

keyMangage: {
title: "Key",
list: "Key List",
name: "Owner",
status: "Status",
operations: "Operations",
check: "Check",
},

priceMangage: {
title: "Price",
},

dataCount: {
title: "DataBoard",
},

apiMangage: {
title: "API",
},
home: {
info: 'Personal Information',
welcome: 'Welcome',
loginAccount: 'Login account',
userName: 'User nickname',
phone: 'Phone',
time: 'Current account duration',
setting: 'security setting',
editPwd: 'Change password',
key: 'secret key',
view: 'View',
},
template: {
img: 'picture'
},
video: {
time: 'Total time consumed',
code: 'Access Party Code',
videoTime: 'Video duration(S)',
videoUrl: 'Video link',
createTime: 'CreateTime',
detail: 'View video details',
},
userList: {
phone: 'phone',
phonePlaceholder: 'Please enter your phone number',
search: 'search',
id: 'user id',
poins: 'Current currency value',
createDate: 'CreateTime',
view: 'View',
rechargeDetail: 'View recharge records',
videoDetail: 'View video list',
personMould: 'Bind custom templates',
personVoice: 'Bind custom voiceprints',
viewDetail: 'Detail',
close: 'Close'
},
personMould: {
cancel: 'Cancel',
confirm: 'Confirm association'
},
personVoice: {
lang: 'language',
country: 'country',
enName: 'English name',
chineseName: 'Chinese name',
cancel: 'Cancel',
confirm: 'Confirm association'
},
rechargeList: {
phone: 'phone',
phonePlaceholder: 'Please enter your phone number',
search: 'search',
productName: 'product name',
productNamePlaceholder: 'Please enter the product name',
time: 'Recharge time period',
orderNumber: 'Order No',
payVendor: 'Payment method',
orderPriceStr: 'Payment amount',
createDate: 'CreateTime'
},
userMouldList: {
phone: 'phone',
phonePlaceholder: 'Please enter your phone number',
search: 'search',
coverImg: 'Cover Image',
mouldSmId: 'Template number',
mouldSmIdPlaceholder: 'Please enter the template number',
bindPhone: 'Bind phone number',
createDate: 'CreateTime'
},
userVoiceList: {
phone: 'phone',
phonePlaceholder: 'Please enter your phone number',
mouldSmId: 'Template number',
mouldSmIdPlaceholder: 'Please enter the template number',
search: 'search',
lang: 'language',
country: 'country',
enName: 'English name',
chineseName: 'Chinese name',
bindPhone: 'Bind phone number',
createDate: 'CreateTime'
},
videoList: {
phone: 'phone',
phonePlaceholder: 'Please enter your phone number',
text: 'text',
textPlaceholder: 'Please enter text',
type: 'type',
typePlaceholder: 'Please select a type',
portrait: 'Portrait',
transversePlate: 'horizontal version',
status: 'status',
statusPlaceholder: 'Please select a status',
notDeleted: 'Not deleted',
deleted: 'Deleted',
time: 'Create time period',
search: 'search',
title: 'Video Title',
coverImg: 'Cover Image',
videoPlayUrl: 'Video address',
videoTime: 'Video duration(S)',
videoSize: 'Video size',
videoMsg: 'Video Generation Information',
costPoints: 'Consumption coins',
costPointsDetail: 'Consumption Gold Coin Details',
createDate: 'CreateTime',
view: 'view',
del: 'delete',
videoDetail: 'Video Details',
name: 'name',
videType: 'Video type',
close: 'Close',
all: 'All'
},
businessCheck: {
detail: 'Key details',
copy: 'copy',
status: 'Key Status',
normal: 'normal',
loseEfficacy: 'lose efficacy',
restore: 'restore',
cancel: 'cancel'
},
editPass: {
username: 'user name',
pass: 'password',
checkPass: 'Confirm Password'
},
apiManage: {
add: 'add',
time: 'Duration (s)',
viewKey: 'View Key',
editAccount: 'Modify login account',
eddAccount: 'Add login account',
setTime: 'Set duration',
associationTemp: 'Association template',
loginAccount: 'Login account',
username: 'user name',
name: 'name',
pass: 'password',
phone: 'phone',
}
};

+ 197
- 6
src/lang/package/zh-cn.ts ファイルの表示

@@ -14,7 +14,7 @@ export default {
},
// 登录页面国际化
login: {
title: "慧影",
title: "邃芒管理端",
username: "用户名",
email: "手机号",
password: "密码",
@@ -23,7 +23,7 @@ export default {
register: "注册",
loginBtn: "登 录",
registerBtn: "注 册",
verifyCode: "验证码",
captcha: "验证码",
// signIn: "已有账号?现在登录!",
// signUp: "没有账号?现在加入!",
signIn: "立即登录",
@@ -52,6 +52,18 @@ export default {
logout: "注销",
goLogin: "登录",
LanguageChange: "语言切换",
home: '首页',
apiManage: 'API接入方管理',
privatization: '私有化客户管理',
template: '模版管理',
secretKey: '密钥查看',
video: '视频制作统计',
userMange: '用户管理',
userList: '用户列表',
userMouldList: '私人定制模板列表',
userVoiceList: '私人定制声纹列表',
rechargeList: '充值列表',
videoList: '视频列表'
},

// 创作视频
@@ -203,10 +215,6 @@ export default {
comingSoon: "敬请期待",
},

apiMangage: {
title: "API 管理",
},

signSucceed: {
title: "确认邮箱",
succeed: "注册成功",
@@ -222,4 +230,187 @@ export default {
modelMangage: {
title: "模板管理",
},

businessMangage: {
title: "商务管理",
name: "所属人",
address: "地址",
status: "状态",
code: "凭证",
addNew: "新增合作商",
createTime: "创建时间",
cancel: "取消",
confirm: "确认",
nameInputText: "请输入所属人名称",
addressInputText: "请输入地址",
codeInputText: "请输入凭证并确保此字段是唯一值",
accessMethod: "接入方式",
typeInputText: "请选择接入方式",
},

keyMangage: {
title: "密钥管理",
list: "密钥列表",
name: "所属人",
status: "密钥状态",
operations: "操作",
check: "查看",
},

priceMangage: {
title: "价格管理",
},

dataCount: {
title: "数据统计",
},

apiMangage: {
title: "API 管理",
},
home: {
info: '个人信息',
welcome: '欢迎您',
loginAccount: '登录账号',
userName: '用户昵称',
phone: '手机号',
time: '当前账户时长',
setting: '安全设置',
editPwd: '修改密码',
key: '密钥',
view: '查看',
},
template: {
img: '图片'
},
video: {
time: '已消耗总时长',
code: '接入方编码',
videoTime: '视频时长(秒)',
videoUrl: '视频链接',
createTime: '创建时间',
detail: '查看视频详情',
},
userList: {
phone: '手机号',
phonePlaceholder: '请输入手机号',
search: '查询',
id: '用户ID',
poins: '当前币值',
createDate: '创建时间',
view: '查看',
rechargeDetail: '查看充值记录',
videoDetail: '查看视频列表',
personMould: '绑定定制模版',
personVoice: '绑定定制声纹',
viewDetail: '查看详情',
close: '关闭'
},
personMould: {
cancel: '取消',
confirm: '确认关联'
},
personVoice: {
lang: '语言',
country: '国家',
enName: '英文名称',
chineseName: '中文名称',
cancel: '取消',
confirm: '确认关联'
},
rechargeList: {
phone: '手机号',
phonePlaceholder: '请输入手机号',
search: '查询',
productName: '商品名称',
productNamePlaceholder: '请输入商品名',
time: '充值时间段',
orderNumber: '订单号',
payVendor: '支付方式',
orderPriceStr: '支付金额',
createDate: '创建时间'
},
userMouldList: {
phone: '手机号',
phonePlaceholder: '请输入手机号',
search: '查询',
coverImg: '封面图',
mouldSmId: '模版号',
mouldSmIdPlaceholder: '请输入模版号',
bindPhone: '绑定手机号',
createDate: '创建时间'
},
userVoiceList: {
phone: '手机号',
phonePlaceholder: '请输入手机号',
mouldSmId: '模版号',
mouldSmIdPlaceholder: '请输入模版号',
search: '查询',
lang: '语言',
country: '国家',
enName: '英文名称',
chineseName: '中文名称',
bindPhone: '绑定手机号',
createDate: '创建时间'
},
videoList: {
phone: '手机号',
phonePlaceholder: '请输入手机号',
text: '文本',
textPlaceholder: '请输入文本',
type: '类型',
typePlaceholder: '请选择类型',
portrait: '竖版',
transversePlate: '横版',
status: '状态',
statusPlaceholder: '请选择状态',
notDeleted: '未删除',
deleted: '已删除',
time: '创建时间段',
search: '查询',
title: '视频标题',
coverImg: '封面图',
videoPlayUrl: '视频地址',
videoTime: '视频时长(秒)',
videoSize: '视频大小',
videoMsg: '视频生成信息',
costPoints: '消耗金币',
costPointsDetail: '消耗金币明细',
createDate: '创建时间',
view: '查看',
del: '删除',
videoDetail: '视频详情',
name: '名称',
videType: '视频类型',
close: '关闭',
all: '全部'
},
businessCheck: {
detail: '密钥详情',
copy: '复制',
status: '密钥状态',
normal: '正常',
loseEfficacy: '失效',
restore: '恢复',
cancel: '作废'
},
editPass: {
username: '用户名',
pass: '密码',
checkPass: '确认密码'
},
apiManage: {
add: '新增',
time: '时长(秒)',
viewKey: '查看密钥',
editAccount: '修改登录账号',
eddAccount: '新增登录账号',
setTime: '设置时长',
associationTemp: '关联模版',
loginAccount: '登录账号',
username: '用户名',
name: '名称',
pass: '密码',
phone: '手机号',
}
};

+ 8
- 0
src/layout/components/AppMain.vue ファイルの表示

@@ -1,3 +1,10 @@
<!--
* @Autor: Chutingting
* @Date: 2023-11-02 11:32:51
* @LastEditors: Chutingting
* @LastEditTime: 2023-11-08 16:06:12
* @Description:
-->
<script setup lang="ts">
import { useTagsViewStore } from "@/store/modules/tagsView";

@@ -23,6 +30,7 @@ const tagsViewStore = useTagsViewStore();
width: 100%;
/* 50= navbar 50 */
min-height: calc(100vh - 80px);
background: #fff;
// overflow: hidden;
// background-color: var(--el-bg-color-page);
// background-color: #000;


+ 0
- 119
src/layout/index copy 2.vue ファイルの表示

@@ -1,119 +0,0 @@
<template>
<div>
<!-- 侧边栏 -->
<div><Sidebar class="sidebar-container" /></div>
<!-- 页面主体 -->
<div>
<!-- nav -->
<div><navbar /></div>
<!-- 页面主体 -->
<div><app-main /></div>
</div>
</div>
</template>

<script setup lang="ts">
import { computed, watchEffect } from "vue";
import { useWindowSize } from "@vueuse/core";
import { AppMain, Navbar } from "./components/index";
import SignSucceed from "../views/signSucceed/index.vue";
import Sidebar from "./components/Sidebar/index.vue";

import { useAppStore } from "@/store/modules/app";
import { useSettingsStore } from "@/store/modules/settings";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const { params } = route;
const { width } = useWindowSize();

/**
* 响应式布局容器固定宽度
*
* 大屏(>=1200px)
* 中屏(>=992px)
* 小屏(>=768px)
*/
const WIDTH = 992;

const appStore = useAppStore();
const settingsStore = useSettingsStore();

const fixedHeader = computed(() => settingsStore.fixedHeader);
const showTagsView = computed(() => settingsStore.tagsView);
const showSettings = computed(() => settingsStore.showSettings);

const classObj = computed(() => ({
hideSidebar: !appStore.sidebar.opened,
openSidebar: appStore.sidebar.opened,
withoutAnimation: appStore.sidebar.withoutAnimation,
mobile: appStore.device === "mobile",
}));

watchEffect(() => {
if (width.value < WIDTH) {
appStore.toggleDevice("mobile");
appStore.closeSideBar(true);
} else {
appStore.toggleDevice("desktop");

if (width.value >= 1200) {
//大屏
appStore.openSideBar(true);
} else {
appStore.closeSideBar(true);
}
}
});

function handleOutsideClick() {
appStore.closeSideBar(false);
}

onMounted(() => {});
</script>

<style lang="scss" scoped>
.app-wrapper {
&::after {
display: table;
clear: both;
content: "";
}

position: relative;
width: 100%;
height: 100%;
e &.mobile.openSidebar {
position: fixed;
top: 0;
}
}

.fixed-header {
position: fixed;
top: 0;
right: 0;
z-index: 9;
width: calc(100% - #{$sideBarWidth});
transition: width 0.28s;
}

.hideSidebar .fixed-header {
width: calc(100% - 54px);
}

.mobile .fixed-header {
width: 100%;
}

.drawer-bg {
position: absolute;
top: 0;
z-index: 999;
width: 100%;
height: 100%;
// background: #1b213a;
background: #000;
opacity: 0.3;
}
</style>

+ 0
- 131
src/layout/index copy.vue ファイルの表示

@@ -1,131 +0,0 @@
<template>
<div :class="classObj" class="app-wrapper">
<!-- 手机设备侧边栏打开遮罩层 -->
<div
v-if="classObj.mobile && classObj.openSidebar"
class="drawer-bg"
@click="handleOutsideClick"
></div>

<Sidebar class="sidebar-container" />

<div :class="{ hasTagsView: showTagsView }" class="main-container">
<div
:class="{ 'fixed-header': fixedHeader }"
style="box-shadow: rgba(0, 0, 0, 0.05) 0px 4px 4px; height: 80px"
>
<navbar />
<tags-view v-if="showTagsView" />
</div>

<!-- 主页面 -->
<app-main />
</div>
</div>
</template>

<script setup lang="ts">
import { computed, watchEffect } from "vue";
import { useWindowSize } from "@vueuse/core";
import { AppMain, Navbar } from "./components/index";
import SignSucceed from "../views/signSucceed/index.vue";
import Sidebar from "./components/Sidebar/index.vue";

import { useAppStore } from "@/store/modules/app";
import { useSettingsStore } from "@/store/modules/settings";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const { params } = route;
const { width } = useWindowSize();

/**
* 响应式布局容器固定宽度
*
* 大屏(>=1200px)
* 中屏(>=992px)
* 小屏(>=768px)
*/
const WIDTH = 992;

const appStore = useAppStore();
const settingsStore = useSettingsStore();

const fixedHeader = computed(() => settingsStore.fixedHeader);
const showTagsView = computed(() => settingsStore.tagsView);
const showSettings = computed(() => settingsStore.showSettings);

const classObj = computed(() => ({
hideSidebar: !appStore.sidebar.opened,
openSidebar: appStore.sidebar.opened,
withoutAnimation: appStore.sidebar.withoutAnimation,
mobile: appStore.device === "mobile",
}));

watchEffect(() => {
if (width.value < WIDTH) {
appStore.toggleDevice("mobile");
appStore.closeSideBar(true);
} else {
appStore.toggleDevice("desktop");

if (width.value >= 1200) {
//大屏
appStore.openSideBar(true);
} else {
appStore.closeSideBar(true);
}
}
});

function handleOutsideClick() {
appStore.closeSideBar(false);
}

onMounted(() => {});
</script>

<style lang="scss" scoped>
.app-wrapper {
&::after {
display: table;
clear: both;
content: "";
}

position: relative;
width: 100%;
height: 100%;
e &.mobile.openSidebar {
position: fixed;
top: 0;
}
}

.fixed-header {
position: fixed;
top: 0;
right: 0;
z-index: 9;
width: calc(100% - #{$sideBarWidth});
transition: width 0.28s;
}

.hideSidebar .fixed-header {
width: calc(100% - 54px);
}

.mobile .fixed-header {
width: 100%;
}

.drawer-bg {
position: absolute;
top: 0;
z-index: 999;
width: 100%;
height: 100%;
// background: #1b213a;
background: #000;
opacity: 0.3;
}
</style>

+ 126
- 30
src/layout/index.vue ファイルの表示

@@ -11,21 +11,55 @@
</div>
<!-- 侧边路由列表 -->
<div class="myRouter">
<div class="myRouter-item" @click="router.push('/createVideo')">
<div class="icon">
<el-icon><i-ep-edit /></el-icon>
</div>
{{ $t("modelMangage.title") }}
</div>

<div class="bottomBtx">
<div class="myRouter-item" @click="router.push('/myAPI')">
<div class="icon">
<el-icon><i-ep-DocumentAdd /></el-icon>
</div>
{{ $t("apiMangage.title") }}
</div>
</div>
<el-menu
default-active="1"
active-text-color="#ffd04b"
background-color="#000"
text-color="#fff"
class="el-menu-vertical-demo">
<el-menu-item index="1" @click="router.push('/home')">
<el-icon><i-ep-House /></el-icon>
<span>{{ $t('navbar.home') }}</span>
</el-menu-item>
<!-- 邃芒后台 -->
<el-menu-item v-if="isAdmin === '1' && localDeploy !== 'true' " index="2" @click="router.push('/apiManage')">
<el-icon><i-ep-link /></el-icon>
<span>{{ $t('navbar.apiManage') }}</span>
</el-menu-item>
<el-menu-item v-if="isAdmin === '1' && localDeploy !== 'true'" index="3" @click="router.push('/privatization')">
<el-icon><i-ep-edit /></el-icon>
<span>{{ $t('navbar.privatization') }}</span>
</el-menu-item>
<!-- 私有化客户管理 API v-if="(isAdmin === '1' && localDeploy === 'true') || isAdmin === '0'" -->
<template v-else>
<el-menu-item index="4" @click="router.push('/template')">
<el-icon><i-ep-edit /></el-icon>
<span>{{ $t('navbar.template') }}</span>
</el-menu-item>
<el-menu-item index="5" @click="router.push('/secretKey')">
<el-icon><i-ep-edit /></el-icon>
<span>{{ $t('navbar.secretKey') }}</span>
</el-menu-item>
<el-menu-item index="6" @click="router.push('/video')">
<el-icon><i-ep-edit /></el-icon>
<span>{{ $t('navbar.video') }}</span>
</el-menu-item>
</template>
<!-- v-if="isAdmin === '1'" -->
<el-sub-menu index="7" v-if="isAdmin == '1'" >
<template #title>
<el-icon><i-ep-edit /></el-icon>
<span>{{ $t('navbar.userMange') }}</span>
</template>
<el-menu-item-group>
<el-menu-item index="7-1" @click="router.push('/userManage')">{{ $t('navbar.userList') }}</el-menu-item>
<el-menu-item index="7-2" @click="router.push('/userMouldList')">{{ $t('navbar.userMouldList') }}</el-menu-item>
<el-menu-item index="7-3" @click="router.push('/userVoiceList')">{{ $t('navbar.userVoiceList') }}</el-menu-item>
<el-menu-item index="7-4" @click="router.push('/rechargeList')">{{ $t('navbar.rechargeList') }}</el-menu-item>
<el-menu-item index="7-5" @click="router.push('/videoList')">{{ $t('navbar.videoList') }}</el-menu-item>
</el-menu-item-group>
</el-sub-menu>
</el-menu>
</div>
</div>
<!-- 右边显示部分 -->
@@ -43,13 +77,13 @@
<lang-select class="navItem" />
</el-tooltip>
<!-- 登录 -->
<div class="navItem" v-if="!AccessToken" @click="router.push('login')">
<div class="navItem" v-if="!isLogin" @click="router.push('login')">
{{ $t("navbar.goLogin") }}
</div>
<!-- 注销 -->
<div
v-if="isLogin"
class="navItem"
v-if="AccessToken"
@click="logout"
style="margin-left: 10px"
>
@@ -77,8 +111,9 @@ import { useAppStore } from "@/store/modules/app";
// import { useSettingsStore } from "@/store/modules/settings";
import { useRoute, useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import { getMenuNavApi } from "@/apis/Permission.js";
import { userInfoModules } from "@/store/modules/userInfo";
import { logoutApi } from '@/apis/login.js'

const userInfoPinia = userInfoModules();
const route = useRoute();
@@ -89,6 +124,10 @@ const AccessToken = localStorage.getItem("AccessToken");
// 当前语言状态与国际化组件
const { locale } = useI18n();
const lanChange = ref(locale);
const isLogin = ref(false);
const isAdmin = ref('')
const localDeploy = ref('')

function logout() {
const msg =
(lanChange.value as string) == "zh-cn"
@@ -101,8 +140,11 @@ function logout() {
confirmButtonText: confirm,
cancelButtonText: cancel,
}).then(() => {
userInfoPinia.loginOut();
router.push(`/login`);
logoutApi().then(res => {
userInfoPinia.loginOut();
localStorage.clear()
router.push(`/login`);
})
localStorage.removeItem("AccessToken");
});
}
@@ -138,19 +180,48 @@ function handleOutsideClick() {
appStore.closeSideBar(false);
}

onMounted(() => {});
onMounted(() => {
setTimeout(() => {
isAdmin.value = localStorage.getItem("isAdmin") || ''
localDeploy.value = localStorage.getItem("localDeploy") || ''
}, 500)
getMenuNavFunc();
});
//#region 联系我们弹窗
const showTalkUsDialog = ref(false);
const updateShowTalkUsDialog = (newValue: any) => {
showTalkUsDialog.value = newValue;
};
//#endregion ------------------------------------

//#region 获取路由列表
const MenuList = ref([{ id: "", name: "" }]);
async function getMenuNavFunc() {
try {
const res = await getMenuNavApi();
MenuList.value = res.data.menuList;
if (res.code === 200) {
isLogin.value = true
}
} catch (error) {
console.log(error);
}
}
//#endregion ------------------------------------

//#region 路由及子路由
const showChildrenRouter = ref("");

//#endregion ------------------------------------



</script>

<!-- 新样式 -->
<style lang="scss" scoped>
.app-wrapper {
width: 1920px;
// width: 1920px;
display: flex;
}
.Sidebar {
@@ -170,8 +241,9 @@ const updateShowTalkUsDialog = (newValue: any) => {
.myRouter {
position: relative;
height: calc(100% - 80px);
padding: 130px 20px 0;
padding: 50px 20px 0;
color: #fff;
width: 230px;
// color: #868593;

.bottomBtx {
@@ -179,10 +251,10 @@ const updateShowTalkUsDialog = (newValue: any) => {
bottom: 30px;
}
&-item {
display: flex;
// display: flex;
width: 100%;
height: 50px;
padding-left: 10px;
// height: 50px;
// padding-left: 10px;
font-size: 20px;
font-weight: 500;
line-height: 50px;
@@ -193,14 +265,20 @@ const updateShowTalkUsDialog = (newValue: any) => {
border-radius: 5px;
margin-bottom: 20px;
margin-right: 20px;
overflow: hidden;
.icon {
width: 40px;
height: 40px;
display: flex;
color: #fff;
border-radius: 50%;
margin-right: 10px;
width: 100%;
height: 50px;
line-height: 50px;

.el-icon {
margin-left: 10px;
margin-top: 12px;
margin-right: 10px;
width: 25px;
height: 25px;
font-size: 25px;
@@ -210,11 +288,22 @@ const updateShowTalkUsDialog = (newValue: any) => {
vertical-align: sub;
}
}

.childrenRouter {
cursor: pointer;
transition: all 0.3s;
}
.childrenRouter:hover {
color: #ffffff;
background-color: #000000;
}
&:hover {
// background: rgba(255, 255, 255, 0.2);

color: #000;
background: #fff;
.icon {
color: #000;
}
.el-icon {
// color: #d4d0ff;
color: #000;
@@ -256,4 +345,11 @@ const updateShowTalkUsDialog = (newValue: any) => {
}
}
}
.el-menu{
border: none;
:deep .el-sub-menu__title, :deep .el-menu-item{
font-size: 16px;
}

}
</style>

+ 1
- 1
src/main.ts ファイルの表示

@@ -17,7 +17,7 @@ import "element-plus/theme-chalk/dark/css-vars.css";
import "@/styles/index.scss";
import "uno.css";
import "@/styles/my.scss"; // 引入自己定义的样式
import 'amfe-flexible';
// import 'amfe-flexible';
import ElementPlus from "element-plus";
// 引入组件
import myLoading from "@/components/myLoading.vue";


+ 25
- 24
src/permission.ts ファイルの表示

@@ -9,35 +9,36 @@ NProgress.configure({ showSpinner: false }); // 进度条
const permissionStore = usePermissionStoreHook();

// 白名单路由
const whiteList = ["/login", "/signSucceed",'/createVideo','/moveImg'];
const whiteList = ["/login", "/signSucceed", "/createVideo", "/moveImg"];

router.beforeEach(async (to, from, next) => {
NProgress.start();
const AccessToken = localStorage.getItem("AccessToken");
next();
// 已登录
if (AccessToken) {
// 重置密码时可以去往login页面
if ((to.params.type as string) == "4") {
next();
}
if (to.query.resetPwd) {
next();
}
if (to.path === "/login") {
next({ path: "/createVideo" });
} else {
next();
}
// 未登录
} else {
// 未登录可以访问白名单页面
if (whiteList.indexOf(to.path) !== -1) {
next();
} else {
// 不在白名单内,跳转登录页
next({ path: "/login" });
}
}
// if (AccessToken) {
// // 重置密码时可以去往login页面
// if ((to.params.type as string) == "4") {
// next();
// }
// if (to.query.resetPwd) {
// next();
// }
// if (to.path === "/login") {
// next({ path: "/createVideo" });
// } else {
// next();
// }
// // 未登录
// } else {
// // 未登录可以访问白名单页面
// if (whiteList.indexOf(to.path) !== -1) {
// next();
// } else {
// // 不在白名单内,跳转登录页
// next({ path: "/login" });
// }
// }
});

router.afterEach(() => {


+ 207
- 36
src/router/index.ts ファイルの表示

@@ -5,87 +5,93 @@ export const Layout = () => import("@/layout/index.vue");

// 静态路由
export const constantRoutes: RouteRecordRaw[] = [

{
path: "/",
redirect: "/login",
},
// 登录
{
path: "/login",
component: () => import("@/views/login/index.vue"),
meta: { hidden: true },
name:'login'
name: "login",
},

{
path: "/signSucceed",
component: () => import("@/views/signSucceed/index.vue"),
meta: { hidden: true },
},
// 创建视频
{
path: "/",
component: Layout,
redirect: "/createVideo",
redirect: "/model",
children: [
{
path: "createVideo",
component: () => import("@/views/createVideo/index.vue"),
name: "createVideo",
path: "model",
component: () => import("@/views/mangage/model.vue"),
name: "model",
meta: {
title: "createVideo",
title: "model",
icon: "homepage",
affix: true,
name: "createVideo.title",
name: "modelMangage.title",
},
},
],
},
// 视频列表
{
path: "/",
component: Layout,
redirect: "/myCreating",
redirect: "/keyList",
children: [
{
path: "myCreating",
component: () => import("@/views/myCreating/index.vue"),
name: "myCreating",
path: "keyList",
component: () => import("@/views/keys/keyList.vue"),
name: "keyList",
meta: {
title: "myCreating",
title: "keyList",
icon: "homepage",
affix: true,
name: "myCreating.title",
name: "keyMangage.list",
},
},
],
}, {
},
{
path: "/",
component: Layout,
redirect: "/myStore",
redirect: "/keyCheck",
children: [
{
path: "myStore",
component: () => import("@/views/myStore/index.vue"),
name: "myStore",
path: "keyCheck",
component: () => import("@/views/keys/keyCheck.vue"),
name: "keyCheck",
meta: {
title: "myStore",
title: "keyCheck",
icon: "homepage",
affix: true,
name: "myStore.title",
name: "keyMangage.title",
},
},
],
}, {
},
{
path: "/",
component: Layout,
redirect: "/myAPI",
redirect: "/businessList",
children: [
{
path: "myAPI",
component: () => import("@/views/myAPI/index.vue"),
name: "myAPI",
path: "businessList",
component: () => import("@/views/business/businessList.vue"),
name: "businessList",
meta: {
title: "myAPI",
title: "businessList",
icon: "homepage",
affix: true,
name: "API.title",
name: "keyMangage.list",
},
},
],
@@ -93,22 +99,187 @@ export const constantRoutes: RouteRecordRaw[] = [
{
path: "/",
component: Layout,
redirect: "/moveImg",
redirect: "/businessCheck",
children: [
{
path: "moveImg",
component: () => import("@/views/moveImg/index.vue"),
name: "moveImg",
path: "businessCheck",
component: () => import("@/views/apiManage/businessCheck.vue"),
name: "businessCheck",
meta: {
title: "moveImg",
title: "businessCheck",
icon: "homepage",
affix: true,
name: "moveImg.title",
name: "keyMangage.title",
},
},
],
},
// 系统设置路由
{
path: "/",
component: Layout,
redirect: "/userList",
children: [
{
path: "userList",
component: () => import("@/views/dict/index.vue"),
name: "userList",
meta: {
title: "userList",
icon: "homepage",
affix: true,
name: "userListMangage.title",
},
},
],
},
{
path: "/",
component: Layout,
redirect: "/ruleList",
children: [
{
path: "ruleList",
component: () => import("@/views/mangage/ruleList.vue"),
name: "ruleList",
meta: {
title: "ruleList",
icon: "homepage",
affix: true,
name: "ruleListMangage.title",
},
},
],
},
{
path: "/",
component: Layout,
redirect: "/home",
children: [
{
path: "/home",
component: () => import("@/views/home/index.vue"),
name: "home",
meta: {
title: "home",
icon: "home",
affix: true,
name: "ruleListMangage.title",
},
},
{
path: "/apiManage",
component: () => import("@/views/apiManage/index.vue"),
name: "apiManage",
meta: {
title: "apiManage",
icon: "homepage",
affix: true,
name: "ruleListMangage.title",
},
},
{
path: "/privatization",
component: () => import("@/views/privatization/index.vue"),
name: "privatization",
meta: {
title: "privatization",
icon: "homepage",
affix: true,
name: "ruleListMangage.title",
},
},
{
path: "/template",
component: () => import("@/views/template/index.vue"),
name: "template",
meta: {
title: "template",
icon: "homepage",
affix: true,
name: "ruleListMangage.title",
},
},
{
path: "/secretKey",
component: () => import("@/views/secretKey/index.vue"),
name: "secretKey",
meta: {
title: "secretKey",
icon: "homepage",
affix: true,
name: "ruleListMangage.title",
},
},
{
path: "/video",
component: () => import("@/views/video/index.vue"),
name: "video",
meta: {
title: "video",
icon: "homepage",
affix: true,
name: "ruleListMangage.title",
},
},
{
path: "/userManage",
component: () => import("@/views/userManage/userList.vue"),
name: "userManage",
meta: {
title: "userManage",
icon: "homepage",
affix: true,
name: "ruleListMangage.title",
},
},
{
path: "/userMouldList",
component: () => import("@/views/userManage/userMouldList.vue"),
name: "userMouldList",
meta: {
title: "userMouldList",
icon: "homepage",
affix: true,
name: "ruleListMangage.title",
},
},
{
path: "/userVoiceList",
component: () => import("@/views/userManage/userVoiceList.vue"),
name: "userVoiceList",
meta: {
title: "userVoiceList",
icon: "homepage",
affix: true,
name: "ruleListMangage.title",
},
},

{
path: "/rechargeList",
component: () => import("@/views/userManage/rechargeList.vue"),
name: "rechargeList",
meta: {
title: "rechargeList",
icon: "homepage",
affix: true,
name: "ruleListMangage.title",
},
},
{
path: "/videoList",
component: () => import("@/views/userManage/videoList.vue"),
name: "videoList",
meta: {
title: "videoList",
icon: "homepage",
affix: true,
name: "ruleListMangage.title",
},
},
],
}
];

/**


+ 7
- 0
src/store/modules/userInfo.js ファイルの表示

@@ -1,3 +1,10 @@
/*
* @Autor: Chutingting
* @Date: 2023-11-02 11:32:51
* @LastEditors: Chutingting
* @LastEditTime: 2023-11-08 10:26:54
* @Description:
*/
import { defineStore } from "pinia";
import { store } from "@/store";
import {


+ 23
- 4
src/types/components.d.ts ファイルの表示

@@ -13,7 +13,12 @@ declare module '@vue/runtime-core' {
CutImg1: typeof import('./../components/cutImg1.vue')['default']
EditPosition: typeof import('./../components/EditPosition.vue')['default']
'EditPosition copy': typeof import('./../components/EditPosition copy.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
@@ -23,27 +28,40 @@ declare module '@vue/runtime-core' {
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElUpload: typeof import('element-plus/es')['ElUpload']
GithubCorner: typeof import('./../components/GithubCorner/index.vue')['default']
Hamburger: typeof import('./../components/Hamburger/index.vue')['default']
IconSelect: typeof import('./../components/IconSelect/index.vue')['default']
IEpAvatar: typeof import('~icons/ep/avatar')['default']
IEpCloseBold: typeof import('~icons/ep/close-bold')['default']
IEpCollection: typeof import('~icons/ep/collection')['default']
IEpDelete: typeof import('~icons/ep/delete')['default']
IEpDocumentAdd: typeof import('~icons/ep/document-add')['default']
IEpDownload: typeof import('~icons/ep/download')['default']
IEpEdit: typeof import('~icons/ep/edit')['default']
IEpGoods: typeof import('~icons/ep/goods')['default']
IEpHouse: typeof import('~icons/ep/house')['default']
IEpLink: typeof import('~icons/ep/link')['default']
IEpLoading: typeof import('~icons/ep/loading')['default']
IEpLock: typeof import('~icons/ep/lock')['default']
IEpMenu: typeof import('~icons/ep/menu')['default']
IEpPhone: typeof import('~icons/ep/phone')['default']
IEpPlus: typeof import('~icons/ep/plus')['default']
IEpPointer: typeof import('~icons/ep/pointer')['default']
IEpRefresh: typeof import('~icons/ep/refresh')['default']
IEpSearch: typeof import('~icons/ep/search')['default']
IEpUser: typeof import('~icons/ep/user')['default']
IEpUserFilled: typeof import('~icons/ep/user-filled')['default']
IEpVideoPlay: typeof import('~icons/ep/video-play')['default']
LangSelect: typeof import('./../components/LangSelect/index.vue')['default']
MultiUpload: typeof import('./../components/Upload/MultiUpload.vue')['default']
@@ -60,5 +78,6 @@ declare module '@vue/runtime-core' {
}
export interface ComponentCustomProperties {
vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
}

+ 0
- 67
src/utils/request copy.js ファイルの表示

@@ -1,67 +0,0 @@
import axios from 'axios'
import router from '@/router/index'
import { ElMessage } from 'element-plus'
// ElMessage
import { userInfoModules } from "@/store/modules/userInfo";
// import stores from '@/store/index'
import { store } from '@/store/index'; // 因为不是在setup环境而是js中,必须重新初始化加载store
const userInfoPinia = userInfoModules(store); //pinia
// 开发环境需以 "/api" 拼接,其他环境需以 "/C" 拼接
const env = process.env.NODE_ENV == 'development' ? "/api" : "/C"
// console.log(process.env.NODE_ENV, 'env')


/**
* @description:发送Axios请求
*/
const request = axios.create({
baseURL: env,
timeout: 15000
})

// 请求拦截器
request.interceptors.request.use(
function (config) {
// 给所有授权的请求提供token
const AccessToken = localStorage.getItem("AccessToken")
if (AccessToken) {
config.headers.token = AccessToken
}
return config
},
function (error) {
// 操作
return Promise.reject(error)
}
)

// 响应拦截器
request.interceptors.response.use(
function (response) {
let code = response.data.code
// 当token无效(过期或损坏)时 1053为未登录
if (code == 1052 || code == 1053) {
localStorage.removeItem("AccessToken");
router.push("/login");
ElMessage.error("登录过期,请重新登录!");
return
// 弹出错误
} else if (code != 200) {
if (code == 500) {
ElMessage.error("系统异常,请稍后再试");

// 其他错误
} else {
return Promise.reject(response.data)
}
} else {
// 正常返回
return response.data
}

},
function (error) {
}
)

export default request

+ 6
- 6
src/utils/request.js ファイルの表示

@@ -11,8 +11,8 @@ const appStore = useAppStore(store); //pinia
const userInfoPinia = userInfoModules(store); //pinia
// const userInfoPinia = userInfoModules(store); //pinia
// 开发环境需以 "/api" 拼接,其他环境需以 "/C" 拼接
const env = process.env.NODE_ENV == 'development' ? "/api" : "/C"
// const env = process.env.NODE_ENV == 'development' ? "" : ""
// const env = process.env.NODE_ENV == 'development' ? "/api" : "/C"
const env = process.env.NODE_ENV == 'development' ? "/A" : "/A"
console.log(env)

/**
@@ -27,7 +27,7 @@ const request = axios.create({
request.interceptors.request.use(

function (config) {
// 给所有授权的请求提供token
// // 给所有授权的请求提供token
const AccessToken = localStorage.getItem("AccessToken")
if (AccessToken) {
config.headers.token = AccessToken
@@ -59,8 +59,8 @@ request.interceptors.response.use(
}
let code = response.data.code
// 当token无效(过期或损坏)时 1053为未登录
if (code == 1052 || code == 1053) {
localStorage.removeItem("AccessToken");
if (code == 1052 || code == 1053 || code === 701) {
// localStorage.removeItem("AccessToken");
router.push("/login");
// ElMessage.error("登录过期,请重新登录!");
// ElMessage.error($t('tips.tokenExpired'));
@@ -78,7 +78,7 @@ request.interceptors.response.use(
);
// 其他错误
} else {
return Promise.reject(response.data)
return Promise.reject(response.data.message)
}
} else {
// 正常返回


+ 5
- 1
src/utils/request.ts ファイルの表示

@@ -21,7 +21,11 @@ service.interceptors.request.use(
return Promise.reject(error);
}
);

declare module 'axios' {
interface AxiosInstance {
(config: AxiosRequestConfig): Promise<any>
}
}
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {


+ 304
- 0
src/views/apiManage/businessCheck.vue ファイルの表示

@@ -0,0 +1,304 @@
<template>
<div class="page flex justify-around">
<div class="card">
<div class="title">{{ $t("businessCheck.detail") }}</div>
<div class="key">
<span class="text">{{ $t("keyMangage.name") }}:{{ name }}</span>
</div>
<!-- appId -->
<div class="key">
<span class="text">appId:{{ showCode }}</span>
<span class="showOrHide" @click="showHideCode">{{ showText }}</span>
<span v-if="showText == '隐藏'" class="copy" @click="copyKey(showCode)"
>{{ $t("businessCheck.copy") }}</span
>
</div>
<!-- appKey -->
<div class="key">
<span class="text">appKey:{{ showCodeII }}</span>
<span class="showOrHide" @click="showHideCodeII">{{ showTextII }}</span>
<span
v-if="showTextII == '隐藏'"
class="copy"
@click="copyKey(showCodeII)"
>{{ $t("businessCheck.copy") }}</span
>
</div>
<!-- signKey -->
<div class="key">
<span class="text">signKey:{{ showCodeIII }}</span>
<span class="showOrHide" @click="showHideCodeIII(showCodeIII)">{{
showTextIII
}}</span>
<span v-if="showTextIII == '隐藏'" class="copy" @click="copyKey"
>{{ $t("businessCheck.copy") }}</span
>
</div>
<div class="keyStatus">
<span class="text"
>{{ $t("businessCheck.status") }}:
<span v-if="!keyStatus" style="color: #36db43">{{ $t("businessCheck.normal") }}</span>
<span v-else style="color: #ff0000">{{ $t("businessCheck.loseEfficacy") }}</span>
</span>
<span v-if="keyStatus" class="fail" @click="changeStatus(0)">{{ $t("businessCheck.restore") }}</span>
<span v-if="!keyStatus" class="success" @click="changeStatus(1)"
>{{ $t("businessCheck.cancel") }}</span
>
</div>
</div>
</div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { linkEmits } from "element-plus";
import { getCurrentInstance } from "vue";

import { useI18n } from "vue-i18n";

import { useRoute, useRouter } from "vue-router";

import { getKeyByIdApi, updateKeyStatusApi } from "@/api_mangage/key";

const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);
const AccessToken = localStorage.getItem("AccessToken"); // 判断有没有登录

const keyId = ref("");
const name = ref("");

const appId = ref("");
const hideCode = ref("");
const showCode = ref("");
const showText = ref("查看");
const detailId = ref("");


const showHideCode = () => {
if (showText.value == "查看") {
showText.value = "隐藏";
showCode.value = appId.value;
} else {
showText.value = "查看";
showCode.value = hideCode.value;
}
};

const appKey = ref("");
const hideCodeII = ref("");
const showCodeII = ref("");
const showTextII = ref("查看");

const showHideCodeII = () => {
if (showTextII.value == "查看") {
showTextII.value = "隐藏";
showCodeII.value = appKey.value;
} else {
showTextII.value = "查看";
showCodeII.value = hideCodeII.value;
}
};

const signKey = ref("");
const hideCodeIII = ref("");
const showCodeIII = ref("");
const showTextIII = ref("查看");

const showHideCodeIII = () => {
if (showTextIII.value == "查看") {
showTextIII.value = "隐藏";
showCodeIII.value = signKey.value;
} else {
showTextIII.value = "查看";
showCodeIII.value = hideCodeIII.value;
}
};

const getKeyFunc = async () => {
const id = keyId.value;
try {
const res = await getKeyByIdApi(id);
console.log(res, "res");
detailId.value = res.data.id
appId.value = res.data.appId;
appKey.value = res.data.appKey;
signKey.value = res.data.signKey;
keyStatus.value = res.data.status;
name.value = res.data.name;

const len = appId.value.length;
const lenII = appKey.value.length;
const lenIII = signKey.value.length;

let code = "";
let codeII = "";
let codeIII = "";
for (let index = 0; index < len; index++) {
code += "*";
}

for (let index = 0; index < lenII; index++) {
codeII += "*";
}

for (let index = 0; index < lenIII; index++) {
codeIII += "*";
}
hideCode.value = code;
showCode.value = code;

hideCodeII.value = codeII;
showCodeII.value = codeII;

hideCodeIII.value = codeIII;
showCodeIII.value = codeIII;
} catch (error) {
console.log(error, "err");
}
};

const copyKey = (text) => {
navigator.clipboard
.writeText(text)
.then(() => {
ElMessage.success("复制成功!");
})
.catch(() => {
ElMessage.error("复制失败!");
});
};

const keyStatus = ref("");

const changeStatus = async (status) => {
const id = detailId.value;
if (status) {
ElMessageBox.confirm("此操作将作废密钥,确定?", "Warning", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
const res = await updateKeyStatusApi(id, status);
if (res.code == 200) {
ElMessage({
type: "success",
message: "作废成功!",
});
getKeyFunc();
}
} catch (error) {
console.log(error, "err");
}
})
.catch(() => {});
} else {
try {
const res = await updateKeyStatusApi(id, status);
if (res.code == 200) {
ElMessage({
type: "success",
message: "恢复成功!",
});
getKeyFunc();
}
} catch (error) {
console.log(error, "err");
}
}
};

onMounted(() => {
keyId.value = route.query.id;
getKeyFunc();
});
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
.card {
width: 80%;
height: 70%;
background-color: #fafafa;
margin-top: 10vh;
border-radius: 5vh;
box-shadow: #0000002e 1px 1px 1px 1px;

.title {
text-align: center;
font-size: 3vh;
padding: 4vh;
}

.key {
text-align: center;
font-size: 2.5vh;
padding: 5vh;
padding-bottom: 0.5vh;
text-align: left;

.copy {
color: #ffffff;
margin-left: 2vh;
cursor: pointer;
transition: all 0.3s;
background-color: #3a57b6;
padding: 0.7vh;
border-radius: 15px;
&:hover {
background-color: #1ceedc;
}
}
.showOrHide {
color: #20b4a8;
margin-left: 2vh;
cursor: pointer;
transition: all 0.3s;
&:hover {
color: #476ad9;
}
}
}
.keyStatus {
text-align: center;
font-size: 2.9vh;
padding: 4vh;
margin-top: 1vh;

.success {
color: #ffffff;
background-color: #ff0000;
margin-left: 2vh;
padding: 0.7vh;
border-radius: 15px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: #36db43;
}
}
.fail {
color: #ffffff;
background-color: #36db43;
margin-left: 2vh;
padding: 0.7vh;
border-radius: 15px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: #ff0000;
}
}
}
}
}
</style>

+ 519
- 0
src/views/apiManage/index.vue ファイルの表示

@@ -0,0 +1,519 @@
<template>
<div class="page justify-around">
<div class="addbtn">
<el-button type="primary" @click="dialogVisible = true"><el-icon><i-ep-plus /></el-icon> {{$t('apiManage.add')}}</el-button>
</div>

<el-table
:data="tableData"
style="width: 100%; height: 90%"
size="large"
align="center"
border
>
<el-table-column
prop="name"
:label="$t('businessMangage.name')"
align="center"
/>

<el-table-column
prop="address"
:label="$t('businessMangage.address')"
align="center"
/>

<el-table-column
prop="status"
:label="$t('businessMangage.status')"
align="center"
>
<template #default="scope">
<span
:style="1 * scope.row.status ? 'color: #ff0000' : 'color: #36db43'"
>{{ getStatus(scope.row.status) }}</span
>
</template>
</el-table-column>

<el-table-column
prop="code"
:label="$t('businessMangage.code')"
align="center"
/>

<el-table-column
prop="createTime"
:label="$t('businessMangage.createTime')"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column
prop="createTime"
:label="$t('apiManage.time')"
align="center"
>
<template #default="scope">
{{ scope.row.remainingTimes }}
</template>
</el-table-column>

<el-table-column
fixed="right"
:label="$t('keyMangage.operations')"
width="400"
align="center"
>
<template #default="scope">
<el-button
link
type="primary"
size="small"
@click="showDetail(scope.row.id)"
>{{$t('apiManage.viewKey')}}</el-button>
<el-button link type="primary" size="small" @click="loginAccount(scope.row)">{{ scope.row.mallUserInfo ? $t('apiManage.editAccount') : $t('apiManage.eddAccount')}}</el-button>
<el-button link type="primary" size="small" @click="setTime(scope.row.id)">{{$t('apiManage.setTime')}}</el-button>
<el-button link type="primary" size="small" @click="associationTemp(scope.row.id)">{{$t('apiManage.associationTemp')}}</el-button>
</template>
</el-table-column>
</el-table>

<el-dialog
v-model="dialogVisible"
v-if="dialogVisible"
:title="$t('businessMangage.addNew')"
width="35%"
:before-close="handleClose"
>
<div class="formData">
<el-form
ref="ruleForm"
label-position="left"
label-width="120px"
:model="formData"
:rules="rules"
>
<el-form-item :label="$t('businessMangage.name') + ':'" prop="name">
<el-input
v-model="formData.name"
:placeholder="$t('businessMangage.nameInputText')"
/>
</el-form-item>

<el-form-item
:label="$t('businessMangage.address') + ':'"
prop="address"
>
<el-input
v-model="formData.address"
:placeholder="$t('businessMangage.addressInputText')"
/>
</el-form-item>

<el-form-item :label="$t('businessMangage.code') + ':'" prop="code">
<el-input
v-model="formData.code"
:placeholder="$t('businessMangage.codeInputText')"
/>
</el-form-item>

<el-form-item
:label="$t('businessMangage.accessMethod') + ':'"
prop="type"
>
<el-select
v-model="formData.type"
:placeholder="$t('businessMangage.typeInputText')"
clearable
disabled
>
<el-option
v-for="(item, index) in accessMethods"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">{{
$t("businessMangage.cancel")
}}</el-button>
<el-button type="primary" @click="addNew(ruleForm)">
{{ $t("businessMangage.confirm") }}
</el-button>
</span>
</template>
</el-dialog>

<!-- 登录账号 -->
<el-dialog v-model="accountDialogVisible" v-if="accountDialogVisible" :title="$t('apiManage.loginAccount')" width="35%" :before-close="handleClose" >
<el-form ref="ruleForm2" label-position="left" label-width="120px" :model="formData2" :rules="rules2">
<el-form-item :label="$t('apiManage.username')" prop="username">
<el-input :disabled="mallUserInfoFlag" v-model="formData2.username" :placeholder="$t('businessMangage.nameInputText')" />
</el-form-item>

<el-form-item :label="$t('apiManage.name')" prop="name" >
<el-input v-model="formData2.name" :placeholder="$t('businessMangage.addressInputText')" />
</el-form-item>

<el-form-item :label="$t('apiManage.pass')" prop="password">
<el-input v-model="formData2.password" :placeholder="$t('businessMangage.codeInputText')" />
</el-form-item>
<el-form-item :label="$t('apiManage.phone')" prop="phone">
<el-input v-model="formData2.phone" :placeholder="$t('businessMangage.codeInputText')" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="accountDialogVisible = false">{{
$t("businessMangage.cancel")
}}</el-button>
<el-button type="primary" @click="addAccount(ruleForm2)">
{{ $t("businessMangage.confirm") }}
</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="modelDialogVisible" v-if="modelDialogVisible" :title="$t('apiManage.associationTemp')" width="60%" :before-close="handleClose" >
<model :serviceId="serviceId" @close="handleClose"></model>
</el-dialog>

<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { linkEmits } from "element-plus";
import { getCurrentInstance } from "vue";

import { businessListApi, addBusinessApi, setRemainingTimes, saveMallUserInfo, getMallUserInfo } from '@/apis/apiManage.js'

import { useI18n } from "vue-i18n";

import dayjs from "dayjs";

import { useRoute, useRouter } from "vue-router";
import model from './model.vue'

const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);

const tableData = ref([]);

const dialogVisible = ref(false);

const showDetail = (id) => {
router.push({
path: "/businessCheck",
query: {
id,
},
});
};

const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);

const getStatus = (val) => {
return val * 1 ? "锁定" : "正常";
};

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD");
};

const handleSizeChange = (val) => {
pageSize.value = val;
getListData(pageNum.value, pageSize.value);
};

const handleCurrentChange = (val) => {
pageNum.value = val;
getListData(pageNum.value, pageSize.value);
};

const getListData = async (PageNum, PageSize) => {
try {
const res = await businessListApi(PageNum, PageSize, 1, route.query.id || '');
console.log(res, "res");
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};

const formData = ref({
name: "",
address: "",
code: "",
type: 1,
});

const rules = {
name: [
{
required: true,
message: "Please input name",
trigger: "blur",
},
],
address: [
{
required: true,
message: "Please input address",
trigger: "blur",
},
],
code: [
{
required: true,
message: "Please input code",
trigger: "blur",
},
],
type: [
{
required: true,
message: "Please select access methods",
trigger: "blur",
},
],
};

const accessMethods = ref([
{
label: "api接入",
value: 1,
},
{
label: "私有化接入",
value: 2,
},
]);

const handleClose = () => {
dialogVisible.value = false;
accountDialogVisible.value = false;
modelDialogVisible.value = false;
setTimeout(() => {
formData.value = {
name: "",
address: "",
code: "",
type: 1,
};
}, 500);
};

const ruleForm = ref();

const addNew = async (refForm) => {
if (!refForm) return;
await refForm.validate(async (valid, fields) => {
if (valid) {
const data = formData.value;
try {
const res = await addBusinessApi(data);
console.log(res, "res");
if (res.code == 200) {
ElMessage.success("添加成功!");
dialogVisible.value = false;
}
getListData(1, 10);
pageNum.value = 1;
pageSize.value = 10;
} catch (error) {
console.log(error, "err");
ElMessage.error(error.message);
}
} else {
ElMessage.error("请完善信息!");
}
});
};

// 设置时长
const setTime = (id) => {
ElMessageBox.prompt('请输入时长(秒)', '设置时长', {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
inputPattern: /^[0-9]*$/,
inputErrorMessage: '请输入数字',
beforeClose: async (action, instance, done) => {
if (action == "confirm") {//点击确认请求接口
let res = await setRemainingTimes({ id: id, times: instance.inputValue })
if (res.code === 200) {
// 通过监测关闭
ElMessage.success('设置成功')
done();
} else {
ElMessage.error(res.message)
}
} else {
//点击取消关闭
done();
}
}
})
}


// 登录账号
const accountDialogVisible = ref(false);
const mallUserInfoFlag = ref(false);
const loginAccount = (item) => {
if (item.mallUserInfo) {
getMallUserInfo(item.id).then(res => {
if (res.code === 200) {
formData2.value = {
serviceId: item.id,
username: res.data.username,
name: res.data.name,
password: '',
phone: res.data.phone
};
}
})
} else {
formData2.value.serviceId = item.id
}
mallUserInfoFlag.value = item.mallUserInfo ? true : false
accountDialogVisible.value = true
}
// 登录账号
const formData2 = ref({
serviceId: "",
username: "",
name: "",
password: "",
phone: "",
});

const rules2 = {
name: [
{
required: true,
message: "Please input name",
trigger: "blur",
},
],
username: [
{
required: true,
message: "Please input username",
trigger: "blur",
},
],
password: [
{
required: true,
message: "Please input password",
trigger: "blur",
},
],
phone: [
{
required: true,
message: "Please input phone",
trigger: "blur",
},
],
};

const ruleForm2 = ref();

const addAccount = async (refForm) => {
if (!refForm) return;
await refForm.validate(async (valid, fields) => {
if (valid) {
const data = formData2.value;
try {
const res = await saveMallUserInfo(data);
if (res.code == 200) {
ElMessage.success("添加成功!");
accountDialogVisible.value = false;
}
getListData(1, 10);
pageNum.value = 1;
pageSize.value = 10;
} catch (error) {
console.log(error, "err");
ElMessage.error(error);
}
} else {
ElMessage.error("请完善信息!");
}
});
};

// 关联模版
const modelDialogVisible = ref(false);
const serviceId = ref('')
function associationTemp(id) {
modelDialogVisible.value = true
serviceId.value = id
}

onMounted(() => {
getListData(1, 10);
});
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}

.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 88
- 0
src/views/apiManage/model.vue ファイルの表示

@@ -0,0 +1,88 @@
<template>
<div>
<el-checkbox-group v-model="checkboxGroup" size="small" class="checkbox">
<el-checkbox v-for="(item, index) in tableData" :key="index" :label="item.id" border>
<img :src="item.coverImg" width="50" />
<p>{{ item.title }}</p>
</el-checkbox>
</el-checkbox-group>
<div style="margin-top: 20px">
<el-button @click="handleClose"
>{{ $t('personMould.cancel') }}</el-button
>
<el-button type="primary" @click="addModel()">{{ $t('personMould.confirm') }}</el-button>
</div>
</div>
</template>

<script setup>
import { ref } from 'vue'
import { ElTable } from 'element-plus'
import { modelAllList, personMouldIdList, associatedMould } from '@/apis/apiManage.js'
import dayjs from "dayjs";

const emit = defineEmits(["close"]);
const props = defineProps({
serviceId: {
required: true,
}
});

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD");
};

const checkboxGroup = ref([])
const total = ref(0);
const tableData = ref([]);

const modelAllListFunc = async () => {
try {
const res = await modelAllList(1, 10000);
const modelIds = await personMouldIdList(props.serviceId);
tableData.value = res.data.list;
total.value = res.data.total * 1;
setTimeout(()=> {
if(modelIds.data && modelIds.data.length > 0) {
modelIds.data.forEach((item) => {
checkboxGroup.value.push(item)
})
}
}, 500)

} catch (error) {
console.log(error, "err");
}
};

const addModel = async () => {
const data = {
mouldIds: checkboxGroup.value,
serviceId: props.serviceId
}
const res = await associatedMould(data);
if(res.code === 200) {
ElMessage.success("关联成功!");
handleClose()
}
};

function handleClose() {
emit("close");
}
onMounted(() => {
modelAllListFunc(1, 10);
});
</script>

<style lang="scss" scoped>
.checkbox {
max-height: 500px;
overflow: auto;
:deep .el-checkbox.el-checkbox--small {
width: 110px;
height:110px;
margin-bottom: 10px;
}
}
</style>

+ 301
- 0
src/views/business/businessCheck.vue ファイルの表示

@@ -0,0 +1,301 @@
<template>
<div class="page flex justify-around">
<div class="card">
<div class="title">密钥详情</div>
<div class="key">
<span class="text">{{ $t("keyMangage.name") }}:{{ name }}</span>
</div>
<!-- appId -->
<div class="key">
<span class="text">appId:{{ showCode }}</span>
<span class="showOrHide" @click="showHideCode">{{ showText }}</span>
<span v-if="showText == '隐藏'" class="copy" @click="copyKey(showCode)"
>复制</span
>
</div>
<!-- appKey -->
<div class="key">
<span class="text">appKey:{{ showCodeII }}</span>
<span class="showOrHide" @click="showHideCodeII">{{ showTextII }}</span>
<span
v-if="showTextII == '隐藏'"
class="copy"
@click="copyKey(showCodeII)"
>复制</span
>
</div>
<!-- signKey -->
<div class="key">
<span class="text">signKey:{{ showCodeIII }}</span>
<span class="showOrHide" @click="showHideCodeIII(showCodeIII)">{{
showTextIII
}}</span>
<span v-if="showTextIII == '隐藏'" class="copy" @click="copyKey"
>复制</span
>
</div>
<div class="keyStatus">
<span class="text"
>密钥状态:
<span v-if="!keyStatus" style="color: #36db43">正常</span>
<span v-else style="color: #ff0000">失效</span>
</span>
<span v-if="keyStatus" class="fail" @click="changeStatus(0)">恢复</span>
<span v-if="!keyStatus" class="success" @click="changeStatus(1)"
>作废</span
>
</div>
</div>
</div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { linkEmits } from "element-plus";
import { getCurrentInstance } from "vue";

import { useI18n } from "vue-i18n";

import { useRoute, useRouter } from "vue-router";

import { getKeyByIdApi, updateKeyStatusApi } from "@/api_mangage/key";

const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);
const AccessToken = localStorage.getItem("AccessToken"); // 判断有没有登录

const keyId = ref("");
const name = ref("");

const appId = ref("");
const hideCode = ref("");
const showCode = ref("");
const showText = ref("查看");

const showHideCode = () => {
if (showText.value == "查看") {
showText.value = "隐藏";
showCode.value = appId.value;
} else {
showText.value = "查看";
showCode.value = hideCode.value;
}
};

const appKey = ref("");
const hideCodeII = ref("");
const showCodeII = ref("");
const showTextII = ref("查看");

const showHideCodeII = () => {
if (showTextII.value == "查看") {
showTextII.value = "隐藏";
showCodeII.value = appKey.value;
} else {
showTextII.value = "查看";
showCodeII.value = hideCodeII.value;
}
};

const signKey = ref("");
const hideCodeIII = ref("");
const showCodeIII = ref("");
const showTextIII = ref("查看");

const showHideCodeIII = () => {
if (showTextIII.value == "查看") {
showTextIII.value = "隐藏";
showCodeIII.value = signKey.value;
} else {
showTextIII.value = "查看";
showCodeIII.value = hideCodeIII.value;
}
};

const getKeyFunc = async () => {
const id = keyId.value;
try {
const res = await getKeyByIdApi(id);
console.log(res, "res");
appId.value = res.data.appId;
appKey.value = res.data.appKey;
signKey.value = res.data.signKey;
keyStatus.value = res.data.status;
name.value = res.data.name;

const len = appId.value.length;
const lenII = appKey.value.length;
const lenIII = signKey.value.length;

let code = "";
let codeII = "";
let codeIII = "";
for (let index = 0; index < len; index++) {
code += "*";
}

for (let index = 0; index < lenII; index++) {
codeII += "*";
}

for (let index = 0; index < lenIII; index++) {
codeIII += "*";
}
hideCode.value = code;
showCode.value = code;

hideCodeII.value = codeII;
showCodeII.value = codeII;

hideCodeIII.value = codeIII;
showCodeIII.value = codeIII;
} catch (error) {
console.log(error, "err");
}
};

const copyKey = (text) => {
navigator.clipboard
.writeText(text)
.then(() => {
ElMessage.success("复制成功!");
})
.catch(() => {
ElMessage.error("复制失败!");
});
};

const keyStatus = ref("");

const changeStatus = async (status) => {
const id = keyId.value;
if (status) {
ElMessageBox.confirm("此操作将作废密钥,确定?", "Warning", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
const res = await updateKeyStatusApi(id, status);
if (res.code == 200) {
ElMessage({
type: "success",
message: "作废成功!",
});
getKeyFunc();
}
} catch (error) {
console.log(error, "err");
}
})
.catch(() => {});
} else {
try {
const res = await updateKeyStatusApi(id, status);
if (res.code == 200) {
ElMessage({
type: "success",
message: "恢复成功!",
});
getKeyFunc();
}
} catch (error) {
console.log(error, "err");
}
}
};

onMounted(() => {
keyId.value = route.query.id;
getKeyFunc();
});
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
.card {
width: 80%;
height: 70%;
background-color: #fafafa;
margin-top: 10vh;
border-radius: 5vh;
box-shadow: #0000002e 1px 1px 1px 1px;

.title {
text-align: center;
font-size: 3vh;
padding: 4vh;
}

.key {
text-align: center;
font-size: 2.5vh;
padding: 5vh;
padding-bottom: 0.5vh;
text-align: left;

.copy {
color: #ffffff;
margin-left: 2vh;
cursor: pointer;
transition: all 0.3s;
background-color: #3a57b6;
padding: 0.7vh;
border-radius: 15px;
&:hover {
background-color: #1ceedc;
}
}
.showOrHide {
color: #20b4a8;
margin-left: 2vh;
cursor: pointer;
transition: all 0.3s;
&:hover {
color: #476ad9;
}
}
}
.keyStatus {
text-align: center;
font-size: 2.9vh;
padding: 4vh;
margin-top: 1vh;

.success {
color: #ffffff;
background-color: #ff0000;
margin-left: 2vh;
padding: 0.7vh;
border-radius: 15px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: #36db43;
}
}
.fail {
color: #ffffff;
background-color: #36db43;
margin-left: 2vh;
padding: 0.7vh;
border-radius: 15px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: #ff0000;
}
}
}
}
}
</style>

+ 351
- 0
src/views/business/businessList.vue ファイルの表示

@@ -0,0 +1,351 @@
<template>
<div class="page justify-around">
<div class="addbtn">
<el-button type="primary" @click="dialogVisible = true">新增</el-button>
</div>

<el-table
:data="tableData"
style="width: 100%; height: 90%"
size="large"
align="center"
>
<el-table-column
prop="name"
:label="$t('businessMangage.name')"
width="200"
align="center"
/>

<el-table-column
prop="address"
:label="$t('businessMangage.address')"
width="200"
align="center"
/>

<el-table-column
prop="status"
:label="$t('businessMangage.status')"
width="200"
align="center"
>
<template #default="scope">
<span
:style="1 * scope.row.status ? 'color: #ff0000' : 'color: #36db43'"
>{{ getStatus(scope.row.status) }}</span
>
</template>
</el-table-column>

<el-table-column
prop="code"
:label="$t('businessMangage.code')"
width="200"
align="center"
/>

<el-table-column
prop="createTime"
:label="$t('businessMangage.createTime')"
width="200"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createTime) }}
</template>
</el-table-column>

<el-table-column
fixed="right"
:label="$t('keyMangage.operations')"
width="200"
align="center"
>
<template #default="scope">
<el-button
link
type="primary"
size="small"
@click="showDetail(scope.row.id)"
>{{ $t("keyMangage.check") }}</el-button
>
</template>
</el-table-column>
</el-table>

<el-dialog
v-model="dialogVisible"
:title="$t('businessMangage.addNew')"
width="35%"
:before-close="handleClose"
>
<div class="formData">
<el-form
ref="ruleForm"
label-position="left"
label-width="120px"
:model="formData"
:rules="rules"
>
<el-form-item :label="$t('businessMangage.name') + ':'" prop="name">
<el-input
v-model="formData.name"
:placeholder="$t('businessMangage.nameInputText')"
/>
</el-form-item>

<el-form-item
:label="$t('businessMangage.address') + ':'"
prop="address"
>
<el-input
v-model="formData.address"
:placeholder="$t('businessMangage.addressInputText')"
/>
</el-form-item>

<el-form-item :label="$t('businessMangage.code') + ':'" prop="code">
<el-input
v-model="formData.code"
:placeholder="$t('businessMangage.codeInputText')"
/>
</el-form-item>

<el-form-item
:label="$t('businessMangage.accessMethod') + ':'"
prop="type"
>
<el-select
v-model="formData.type"
:placeholder="$t('businessMangage.typeInputText')"
clearable
>
<el-option
v-for="(item, index) in accessMethods"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">{{
$t("businessMangage.cancel")
}}</el-button>
<el-button type="primary" @click="addNew(ruleForm)">
{{ $t("businessMangage.confirm") }}
</el-button>
</span>
</template>
</el-dialog>

<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { linkEmits } from "element-plus";
import { getCurrentInstance } from "vue";

import { businessListApi, addBusinessApi } from "@/api_mangage/business.js";

import { useI18n } from "vue-i18n";

import dayjs from "dayjs";

import { useRoute, useRouter } from "vue-router";

const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);

const tableData = ref([]);

const dialogVisible = ref(false);

const showDetail = (id) => {
router.push({
path: "/businessCheck",
query: {
id,
},
});
};

const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);

const getStatus = (val) => {
return val * 1 ? "锁定" : "正常";
};

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD");
};

const handleSizeChange = (val) => {
pageSize.value = val;
getBusinessListFunc(pageNum.value, pageSize.value);
};

const handleCurrentChange = (val) => {
pageNum.value = val;
getBusinessListFunc(pageNum.value, pageSize.value);
};

const getBusinessListFunc = async (PageNum, PageSize) => {
try {
const res = await businessListApi(PageNum, PageSize);
console.log(res, "res");
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};

const formData = ref({
name: "",
address: "",
code: "",
type: "",
});

const rules = {
name: [
{
required: true,
message: "Please input name",
trigger: "blur",
},
],
address: [
{
required: true,
message: "Please input address",
trigger: "blur",
},
],
code: [
{
required: true,
message: "Please input code",
trigger: "blur",
},
],
type: [
{
required: true,
message: "Please select access methods",
trigger: "blur",
},
],
};

const accessMethods = ref([
{
label: "api接入",
value: 1,
},
{
label: "私有化接入",
value: 2,
},
]);

const handleClose = () => {
dialogVisible.value = false;
setTimeout(() => {
formData.value = {
name: "",
address: "",
code: "",
type: "",
};
}, 500);
};

const ruleForm = ref();

const addNew = async (refForm) => {
if (!refForm) return;
await refForm.validate(async (valid, fields) => {
if (valid) {
const data = formData.value;
try {
const res = await addBusinessApi(data);
console.log(res, "res");
if (res.code == 200) {
ElMessage.success("添加成功!");
dialogVisible.value = false;
}
getBusinessListFunc(1, 10);
pageNum.value = 1;
pageSize.value = 10;
} catch (error) {
console.log(error, "err");
ElMessage.error(error.message);
}
} else {
ElMessage.error("请完善信息!");
}
});
};

onMounted(() => {
getBusinessListFunc(1, 10);
});
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}

.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 2
- 2
src/views/createVideo/index.vue ファイルの表示

@@ -757,8 +757,8 @@ const soundStyle = ref(""); //选择的风格
// 语言下拉框内容
const languageOptions = ref([]);
async function voiceTotal() {
const res = await voiceTotalApi();
languageOptions.value = res.data;
// const res = await voiceTotalApi();
// languageOptions.value = res.data;
}
// 声音下拉框内容
const soundOptions = ref([]);


+ 325
- 0
src/views/dict/components/dict-item.vue ファイルの表示

@@ -0,0 +1,325 @@
<!-- 字典数据 -->
<script setup lang="ts">
import {
getDictPage,
getDictFormData,
addDict,
updateDict,
deleteDict,
} from "@/api/dict";
import { DictPageVO, DictForm, DictQuery } from "@/api/dict/types";

defineOptions({
name: "DictData",
inheritAttrs: false,
});

const props = defineProps({
typeCode: {
type: String,
default: () => {
return "";
},
},
typeName: {
type: String,
default: () => {
return "";
},
},
});

watch(
() => props.typeCode,
(newVal: string) => {
queryParams.typeCode = newVal;
formData.typeCode = newVal;
resetQuery();
}
);

const queryFormRef = ref(ElForm);
const dataFormRef = ref(ElForm);

const loading = ref(false);
const ids = ref<number[]>([]);
const total = ref(0);

const queryParams = reactive<DictQuery>({
pageNum: 1,
pageSize: 10,
typeCode: props.typeCode,
});

const dictList = ref<DictPageVO[]>();

const dialog = reactive<DialogOption>({
visible: false,
});

const formData = reactive<DictForm>({
status: 1,
typeCode: props.typeCode,
sort: 1,
});

const rules = reactive({
name: [{ required: true, message: "请输入字典名称", trigger: "blur" }],
value: [{ required: true, message: "请输入字典值", trigger: "blur" }],
});

/**
* 查询
*/
function handleQuery() {
if (queryParams.typeCode) {
loading.value = true;
getDictPage(queryParams)
.then(({ data }) => {
dictList.value = data.list;
total.value = data.total;
})
.finally(() => (loading.value = false));
}
}

/**
* 重置查询
*/
function resetQuery() {
queryFormRef.value.resetFields();
queryParams.pageNum = 1;
handleQuery();
}

/**
* 行checkbox change事件
*
* @param selection
*/
function handleSelectionChange(selection: any) {
ids.value = selection.map((item: any) => item.id);
}

/**
* 打开字典表单弹窗
*
* @param dictId 字典ID
*/
function openDialog(dictId?: number) {
dialog.visible = true;
if (dictId) {
dialog.title = "修改字典";
getDictFormData(dictId).then(({ data }) => {
Object.assign(formData, data);
});
} else {
dialog.title = "新增字典";
}
}

/**
* 字典表单提交
*/
function handleSubmit() {
dataFormRef.value.validate((isValid: boolean) => {
if (isValid) {
loading.value = false;
const dictId = formData.id;
if (dictId) {
updateDict(dictId, formData)
.then(() => {
ElMessage.success("修改成功");
closeDialog();
resetQuery();
})
.finally(() => (loading.value = false));
} else {
addDict(formData)
.then(() => {
ElMessage.success("新增成功");
closeDialog();
resetQuery();
})
.finally(() => (loading.value = false));
}
}
});
}

/**
* 关闭弹窗
*/
function closeDialog() {
dialog.visible = false;
resetForm();
}

/**
* 重置表单
*/
function resetForm() {
dataFormRef.value.resetFields();
dataFormRef.value.clearValidate();

formData.id = undefined;
formData.status = 1;
formData.sort = 1;
formData.typeCode = props.typeCode;
}

/**
* 删除字典
*/
function handleDelete(dictId?: number) {
const dictIds = [dictId || ids.value].join(",");
if (!dictIds) {
ElMessage.warning("请勾选删除项");
return;
}

ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
deleteDict(dictIds).then(() => {
ElMessage.success("删除成功");
resetQuery();
});
});
}

onMounted(() => {
handleQuery();
});
</script>

<template>
<div class="app-container">
<div class="search-container">
<!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="关键字" prop="name">
<el-input
v-model="queryParams.name"
placeholder="字典名称"
clearable
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery"
><i-ep-search />搜索</el-button
>
<el-button @click="resetQuery"> <i-ep-refresh />重置</el-button>
</el-form-item>
</el-form>
</div>
<el-card shadow="never">
<template #header>
<el-button
v-hasPerm="['sys:dict:add']"
type="success"
@click="openDialog()"
><i-ep-plus />新增</el-button
>
<el-button
v-hasPerm="['sys:dict:delete']"
type="danger"
:disabled="ids.length === 0"
@click="handleDelete()"
><i-ep-delete />删除</el-button
>
</template>

<!-- 数据表格 -->
<el-table
v-loading="loading"
:data="dictList"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="50" />
<el-table-column label="字典名称" prop="name" />
<el-table-column label="字典值" prop="value" />
<el-table-column label="状态" align="center">
<template #default="scope">
<el-tag v-if="scope.row.status === 1" type="success">启用</el-tag>
<el-tag v-else type="info">禁用</el-tag>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center">
<template #default="scope">
<el-button
v-hasPerm="['sys:dict:edit']"
type="primary"
link
@click="openDialog(scope.row.id)"
><i-ep-edit />编辑</el-button
>
<el-button
v-hasPerm="['sys:dict:delete']"
type="primary"
link
@click.stop="handleDelete(scope.row.id)"
><i-ep-delete />删除</el-button
>
</template>
</el-table-column>
</el-table>

<pagination
v-if="total > 0"
v-model:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
</el-card>

<!-- 表单弹窗 -->
<el-dialog
v-model="dialog.visible"
:title="dialog.title"
width="500px"
@close="closeDialog"
>
<el-form
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="100px"
>
<el-form-item label="字典名称">{{ typeName }}</el-form-item>
<el-form-item label="字典名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入字典名称" />
</el-form-item>
<el-form-item label="字典值" prop="value">
<el-input v-model="formData.value" placeholder="字典值" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number
v-model="formData.sort"
controls-position="right"
:min="0"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleSubmit">确 定</el-button>
<el-button @click="closeDialog">取 消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>

+ 322
- 0
src/views/dict/index.vue ファイルの表示

@@ -0,0 +1,322 @@
<template>
<div class="app-container">
<div class="search-container">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="关键字" prop="name">
<el-input
v-model="queryParams.keywords"
placeholder="字典类型名称/编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery()"
><i-ep-search />搜索</el-button
>
<el-button @click="resetQuery()"><i-ep-refresh />重置</el-button>
</el-form-item>
</el-form>
</div>

<el-card shadow="never" class="table-container">
<template #header>
<el-button
v-hasPerm="['sys:dict_type:add']"
type="success"
@click="openDialog()"
><i-ep-plus />新增</el-button
>
<el-button
type="danger"
:disabled="ids.length === 0"
@click="handleDelete()"
><i-ep-delete />删除</el-button
>
</template>
<el-table
v-loading="loading"
highlight-current-row
:data="dictTypeList"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="字典类型名称" prop="name" width="200" />
<el-table-column label="字典类型编码" prop="code" width="200" />
<el-table-column label="状态" align="center" width="100">
<template #default="scope">
<el-tag v-if="scope.row.status === 1" type="success">启用</el-tag>
<el-tag v-else type="info">禁用</el-tag>
</template>
</el-table-column>
<el-table-column label="备注" prop="remark" align="center" />
<el-table-column fixed="right" label="操作" align="center" width="220">
<template #default="scope">
<el-button
type="primary"
link
size="small"
@click.stop="openDictDialog(scope.row)"
><i-ep-Collection />字典数据</el-button
>
<el-button
v-hasPerm="['sys:dict_type:edit']"
type="primary"
link
size="small"
@click.stop="openDialog(scope.row.id)"
><i-ep-edit />编辑</el-button
>
<el-button
v-hasPerm="['sys:dict_type:delete']"
type="primary"
link
size="small"
@click.stop="handleDelete(scope.row.id)"
><i-ep-delete />删除</el-button
>
</template>
</el-table-column>
</el-table>

<pagination
v-if="total > 0"
v-model:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
</el-card>

<el-dialog
v-model="dialog.visible"
:title="dialog.title"
width="500px"
@close="closeDialog"
>
<el-form
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="80px"
>
<el-form-item label="字典名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入字典名称" />
</el-form-item>
<el-form-item label="字典编码" prop="code">
<el-input v-model="formData.code" placeholder="请输入字典编码" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="formData.remark"
type="textarea"
placeholder="字典类型备注"
:autosize="{ minRows: 2, maxRows: 4 }"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleSubmit">确 定</el-button>
<el-button @click="closeDialog">取 消</el-button>
</div>
</template>
</el-dialog>

<!--字典数据弹窗-->
<el-dialog
v-model="dictDataDialog.visible"
:title="dictDataDialog.title"
width="1000px"
@close="closeDictDialog"
>
<dict-item
v-model:typeCode="selectedDictType.typeCode"
v-model:typeName="selectedDictType.typeName"
/>
</el-dialog>
</div>
</template>

<script setup>
import {
getDictTypePage,
getDictTypeForm,
addDictType,
updateDictType,
deleteDictTypes,
} from "@/api/dict";

// import { DictTypePageVO, DictTypeQuery, DictTypeForm } from "@/api/dict/types";

defineOptions({
name: "DictType",
inheritAttrs: false,
});

const queryFormRef = ref("");
const dataFormRef = ref("");

const loading = ref(false);
const ids = ref([]);
const total = ref(0);

const queryParams = reactive({
pageNum: 1,
pageSize: 10,
});

const dictTypeList = ref();

const dialog = reactive({
visible: false,
});

const formData = reactive({
status: 1,
});

const rules = reactive({
name: [{ required: true, message: "请输入字典类型名称", trigger: "blur" }],
code: [{ required: true, message: "请输入字典类型编码", trigger: "blur" }],
});

/** 查询 */
function handleQuery() {
loading.value = true;
getDictTypePage(queryParams)
.then(({ data }) => {
dictTypeList.value = data.list;
total.value = data.total;
})
.finally(() => {
loading.value = false;
});
}

/**
* 重置查询
*/
function resetQuery() {
queryFormRef.value.resetFields();
queryParams.pageNum = 1;
handleQuery();
}

/** 行复选框选中 */
function handleSelectionChange(selection) {
ids.value = selection.map((item) => item.id);
}

/**
* 打开字典类型表单弹窗
*
* @param dicTypeId 字典类型ID
*/
function openDialog(dicTypeId) {
dialog.visible = true;
if (dicTypeId) {
dialog.title = "修改字典类型";
getDictTypeForm(dicTypeId).then(({ data }) => {
Object.assign(formData, data);
});
} else {
dialog.title = "新增字典类型";
}
}

/** 字典类型表单提交 */
function handleSubmit() {
dataFormRef.value.validate((isValid) => {
if (isValid) {
loading.value = false;
const dictTypeId = formData.id;
if (dictTypeId) {
updateDictType(dictTypeId, formData)
.then(() => {
ElMessage.success("修改成功");
closeDialog();
handleQuery();
})
.finally(() => (loading.value = false));
} else {
addDictType(formData)
.then(() => {
ElMessage.success("新增成功");
closeDialog();
handleQuery();
})
.finally(() => (loading.value = false));
}
}
});
}

/** 关闭字典类型弹窗 */
function closeDialog() {
dialog.visible = false;
resetForm();
}

/** 重置字典类型表单 */
function resetForm() {
dataFormRef.value.resetFields();
dataFormRef.value.clearValidate();

formData.id = undefined;
formData.status = 1;
}

/** 删除字典类型 */
function handleDelete(dictTypeId) {
const dictTypeIds = [dictTypeId || ids.value].join(",");
if (!dictTypeIds) {
ElMessage.warning("请勾选删除项");
return;
}

ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
deleteDictTypes(dictTypeIds).then(() => {
ElMessage.success("删除成功");
resetQuery();
});
});
}

const dictDataDialog = reactive({
visible: false,
});

const selectedDictType = reactive({ typeCode: "", typeName: "" }); // 当前选中的字典类型

/** 打开字典数据弹窗 */
function openDictDialog(row) {
dictDataDialog.visible = true;
dictDataDialog.title = "【" + row.name + "】字典数据";

selectedDictType.typeCode = row.code;
selectedDictType.typeName = row.name;
}

/** 关闭字典数据弹窗 */
function closeDictDialog() {
dictDataDialog.visible = false;
}

onMounted(() => {
handleQuery();
});
</script>

<style lang="scss" scoped></style>

+ 116
- 0
src/views/editPass/index.vue ファイルの表示

@@ -0,0 +1,116 @@
<template>
<div class="page">
<el-form
ref="ruleFormRef"
:model="ruleForm"
status-icon
:rules="rules"
label-width="120px"
class="editPass"
>
<el-form-item :label="$t('editPass.username')" prop="pass">
<el-input v-model="ruleForm.userName" disabled autocomplete="off" />
</el-form-item>
<el-form-item :label="$t('editPass.pass')" prop="pass">
<el-input v-model="ruleForm.pass" type="password" autocomplete="off" />
</el-form-item>
<el-form-item :label="$t('editPass.checkPass')" prop="checkPass">
<el-input
v-model="ruleForm.checkPass"
type="password"
autocomplete="off"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleClose(ruleFormRef)">Close</el-button>
<el-button type="primary" @click="submitForm(ruleFormRef)">Submit</el-button>
</el-form-item>
</el-form>
</div>
</template>

<script lang="ts" setup>
import { reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { updatepwd } from "@/apis/editPass.js";
import { logoutApi } from '@/apis/login.js'
import { userInfoModules } from "@/store/modules/userInfo";
import { useRoute, useRouter } from "vue-router";

import { useI18n } from "vue-i18n";
const { locale } = useI18n();
const lanChange = ref(locale);

const userInfoPinia = userInfoModules();
const router = useRouter();
const emit = defineEmits(["close"]);

const ruleFormRef = ref<FormInstance>()

const validatePass = (rule: any, value: any, callback: any) => {
if (value === '') {
callback(new Error(lanChange.value == "zh-cn" ? '请输入密码' : 'Please enter the password'))
} else {
if (ruleForm.checkPass !== '') {
if (!ruleFormRef.value) return
ruleFormRef.value.validateField('checkPass', () => null)
}
callback()
}
}
const validatePass2 = (rule: any, value: any, callback: any) => {
if (value === '') {
callback(new Error(lanChange.value == "zh-cn" ? '请重新输入密码' : 'Please re-enter the password'))
} else if (value !== ruleForm.pass) {
callback(new Error(lanChange.value == "zh-cn" ? "两次密码不一致" : 'The two passwords do not match'))
} else {
callback()
}
}

const ruleForm = reactive({
userName: localStorage.getItem("username"),
pass: '',
checkPass: ''
})

const rules = reactive({
pass: [{ validator: validatePass, trigger: 'blur' }],
checkPass: [{ validator: validatePass2, trigger: 'blur' }]
})

const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate(async (valid) => {
if (valid) {
const data = {
userName: ruleForm.userName,
pwd: ruleForm.checkPass
};
try {
const res = await updatepwd(data);
if (res.code == 200) {
ElMessage.success( lanChange.value == "zh-cn" ? "修改成功!" : 'Modified successfully!');
logoutApi().then(d => {
userInfoPinia.loginOut();
router.push(`/login`);
})
localStorage.removeItem("AccessToken");
handleClose(ruleFormRef.value)
}
} catch (error) {
console.log(error, "err");
// ElMessage.error(error.message);
}
} else {
console.log('error submit!')
return false
}
})
}
const handleClose = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
emit("close");
}
</script>

+ 104
- 0
src/views/home/index.vue ファイルの表示

@@ -0,0 +1,104 @@
<template>
<div class="page">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>{{ $t('home.info') }}</span>
</div>
<div>
<div style="text-align: center">
<div class="el-upload">
<img :src="Avatar" class="avatar">
<p>{{ userData.username }},{{ $t('home.welcome') }}</p>
</div>
</div>
<ul class="user-info">
<li><div style="height: 100%"><el-icon><i-ep-UserFilled /></el-icon> {{ $t('home.loginAccount') }}<div class="user-right">{{ userData.username }}</div></div></li>
<li><el-icon><i-ep-Avatar /></el-icon> {{ $t('home.userName') }} <div class="user-right">{{ userData.name }}</div></li>
<li><el-icon><i-ep-User /></el-icon> {{ $t('home.phone') }} <div class="user-right"> {{ userData.phone }}</div></li>
<li v-if="(userData.isAdmin === 1 && localDeploy) || userData.isAdmin === 0"><el-icon><i-ep-User /></el-icon> {{ $t('home.time') }} <div class="user-right"> {{ userData.remainingTimes }} (秒)</div></li>
<li>
<el-icon><i-ep-edit /></el-icon> {{ $t('home.setting') }}
<div class="user-right">
<a @click="editDialogVisible = true">{{ $t('home.editPwd') }}</a>
</div>
</li>
<!-- <li v-if="userData.isAdmin === '1' && userData.localDeploy">
<el-icon><i-ep-Pointer /></el-icon> 视频制作统计
<div class="user-right">
<a @click="videoDialogVisible = true">查看</a>
</div>
</li> -->
<li v-if="userData.isAdmin === 0">
<el-icon><i-ep-Pointer /></el-icon>{{ $t('home.key') }}
<div class="user-right">
<a @click="router.push('/secretKey')">{{ $t('home.view') }} </a>
</div>
</li>
</ul>
</div>
</el-card>
<el-dialog v-model="editDialogVisible" :title="$t('home.editPwd')" width="40%" :before-close="handleClose" >
<edit-pass @close="handleClose"></edit-pass>
</el-dialog>
</div>
</template>
<script setup>
import editPass from '../editPass/index.vue'
import { useRoute, useRouter } from "vue-router";
import { getUser, privateDeployAdminCurrent, current, localDeployApi } from "@/apis/home.js";
const route = useRoute();
const router = useRouter();
const Avatar = ref('https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png')
const editDialogVisible = ref(false)
const handleClose = () => {
editDialogVisible.value = false;
};

const userData = ref({
username: '',
name: '',
remainingTimes: '',
phone: '',
isAdmin: '',
localDeploy: ''
});
const localDeploy = ref('')
const getData = async () => {
let userRes = await getUser()
let localDeployRes = await localDeployApi()
userData.value = userRes.data
localStorage.setItem("username", userRes.data.username);
localStorage.setItem("isAdmin", userRes.data.isAdmin); // isAdmin=1,为管理人员(邃芒后台) , isAdmin=0,为API接入方登录后台 isAmin=1&&localDeploy=true,就是私有化部署的管理后台
userData.value.localDeploy = localDeployRes.data
localDeploy.value = localDeployRes.data
localStorage.setItem("localDeploy", localDeployRes.data);
// 当前账户时长
let res = userData.isAdmin === 1 && localDeploy ? await privateDeployAdminCurrent() : await current()
userData.value.remainingTimes = res.data.remainingTimes
}

onMounted(async () => {
getData()
});
</script>
<style lang="scss" scoped>
.page{
padding: 30px
}
.user-info {
padding-left: 0;
list-style: none;
li{
border-bottom: 1px solid #F0F3F4;
padding: 11px 0;
font-size: 13px;
}
.user-right {
float: right;
a{
color: #317EF3;
}
}
}
</style>

+ 301
- 0
src/views/keys/keyCheck.vue ファイルの表示

@@ -0,0 +1,301 @@
<template>
<div class="page flex justify-around">
<div class="card">
<div class="title">密钥详情</div>
<div class="key">
<span class="text">{{ $t("keyMangage.name") }}:{{ name }}</span>
</div>
<!-- appId -->
<div class="key">
<span class="text">appId:{{ showCode }}</span>
<span class="showOrHide" @click="showHideCode">{{ showText }}</span>
<span v-if="showText == '隐藏'" class="copy" @click="copyKey(showCode)"
>复制</span
>
</div>
<!-- appKey -->
<div class="key">
<span class="text">appKey:{{ showCodeII }}</span>
<span class="showOrHide" @click="showHideCodeII">{{ showTextII }}</span>
<span
v-if="showTextII == '隐藏'"
class="copy"
@click="copyKey(showCodeII)"
>复制</span
>
</div>
<!-- signKey -->
<div class="key">
<span class="text">signKey:{{ showCodeIII }}</span>
<span class="showOrHide" @click="showHideCodeIII(showCodeIII)">{{
showTextIII
}}</span>
<span v-if="showTextIII == '隐藏'" class="copy" @click="copyKey"
>复制</span
>
</div>
<div class="keyStatus">
<span class="text"
>密钥状态:
<span v-if="!keyStatus" style="color: #36db43">正常</span>
<span v-else style="color: #ff0000">失效</span>
</span>
<span v-if="keyStatus" class="fail" @click="changeStatus(0)">恢复</span>
<span v-if="!keyStatus" class="success" @click="changeStatus(1)"
>作废</span
>
</div>
</div>
</div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { linkEmits } from "element-plus";
import { getCurrentInstance } from "vue";

import { useI18n } from "vue-i18n";

import { useRoute, useRouter } from "vue-router";

import { getKeyByIdApi, updateKeyStatusApi } from "@/api_mangage/key";

const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);
const AccessToken = localStorage.getItem("AccessToken"); // 判断有没有登录

const keyId = ref("");
const name = ref("");

const appId = ref("");
const hideCode = ref("");
const showCode = ref("");
const showText = ref("查看");

const showHideCode = () => {
if (showText.value == "查看") {
showText.value = "隐藏";
showCode.value = appId.value;
} else {
showText.value = "查看";
showCode.value = hideCode.value;
}
};

const appKey = ref("");
const hideCodeII = ref("");
const showCodeII = ref("");
const showTextII = ref("查看");

const showHideCodeII = () => {
if (showTextII.value == "查看") {
showTextII.value = "隐藏";
showCodeII.value = appKey.value;
} else {
showTextII.value = "查看";
showCodeII.value = hideCodeII.value;
}
};

const signKey = ref("");
const hideCodeIII = ref("");
const showCodeIII = ref("");
const showTextIII = ref("查看");

const showHideCodeIII = () => {
if (showTextIII.value == "查看") {
showTextIII.value = "隐藏";
showCodeIII.value = signKey.value;
} else {
showTextIII.value = "查看";
showCodeIII.value = hideCodeIII.value;
}
};

const getKeyFunc = async () => {
const id = keyId.value;
try {
const res = await getKeyByIdApi(id);
console.log(res, "res");
appId.value = res.data.appId;
appKey.value = res.data.appKey;
signKey.value = res.data.signKey;
keyStatus.value = res.data.status;
name.value = res.data.name;

const len = appId.value.length;
const lenII = appKey.value.length;
const lenIII = signKey.value.length;

let code = "";
let codeII = "";
let codeIII = "";
for (let index = 0; index < len; index++) {
code += "*";
}

for (let index = 0; index < lenII; index++) {
codeII += "*";
}

for (let index = 0; index < lenIII; index++) {
codeIII += "*";
}
hideCode.value = code;
showCode.value = code;

hideCodeII.value = codeII;
showCodeII.value = codeII;

hideCodeIII.value = codeIII;
showCodeIII.value = codeIII;
} catch (error) {
console.log(error, "err");
}
};

const copyKey = (text) => {
navigator.clipboard
.writeText(text)
.then(() => {
ElMessage.success("复制成功!");
})
.catch(() => {
ElMessage.error("复制失败!");
});
};

const keyStatus = ref("");

const changeStatus = async (status) => {
const id = keyId.value;
if (status) {
ElMessageBox.confirm("此操作将作废密钥,确定?", "Warning", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
try {
const res = await updateKeyStatusApi(id, status);
if (res.code == 200) {
ElMessage({
type: "success",
message: "作废成功!",
});
getKeyFunc();
}
} catch (error) {
console.log(error, "err");
}
})
.catch(() => {});
} else {
try {
const res = await updateKeyStatusApi(id, status);
if (res.code == 200) {
ElMessage({
type: "success",
message: "恢复成功!",
});
getKeyFunc();
}
} catch (error) {
console.log(error, "err");
}
}
};

onMounted(() => {
keyId.value = route.query.id;
getKeyFunc();
});
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
.card {
width: 80%;
height: 70%;
background-color: #fafafa;
margin-top: 10vh;
border-radius: 5vh;
box-shadow: #0000002e 1px 1px 1px 1px;

.title {
text-align: center;
font-size: 3vh;
padding: 4vh;
}

.key {
text-align: center;
font-size: 2.5vh;
padding: 5vh;
padding-bottom: 0.5vh;
text-align: left;

.copy {
color: #ffffff;
margin-left: 2vh;
cursor: pointer;
transition: all 0.3s;
background-color: #3a57b6;
padding: 0.7vh;
border-radius: 15px;
&:hover {
background-color: #1ceedc;
}
}
.showOrHide {
color: #20b4a8;
margin-left: 2vh;
cursor: pointer;
transition: all 0.3s;
&:hover {
color: #476ad9;
}
}
}
.keyStatus {
text-align: center;
font-size: 2.9vh;
padding: 4vh;
margin-top: 1vh;

.success {
color: #ffffff;
background-color: #ff0000;
margin-left: 2vh;
padding: 0.7vh;
border-radius: 15px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: #36db43;
}
}
.fail {
color: #ffffff;
background-color: #36db43;
margin-left: 2vh;
padding: 0.7vh;
border-radius: 15px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: #ff0000;
}
}
}
}
}
</style>

+ 149
- 0
src/views/keys/keyList.vue ファイルの表示

@@ -0,0 +1,149 @@
<template>
<div class="page flex justify-around">
<el-table
:data="tableData"
style="width: 100%; height: 90%"
size="large"
align="center"
>
<el-table-column
prop="name"
:label="$t('keyMangage.name')"
width="220"
align="center"
/>

<el-table-column
prop="status"
:label="$t('keyMangage.status')"
width="220"
align="center"
>
<template #default="scope">
<span
:style="1 * scope.row.status ? 'color: #ff0000' : 'color: #36db43'"
>{{ getStatus(scope.row.status) }}</span
>
</template>
</el-table-column>

<el-table-column
fixed="right"
:label="$t('keyMangage.operations')"
width="220"
align="center"
>
<template #default="scope">
<el-button
link
type="primary"
size="small"
@click="showDetail(scope.row.id)"
>{{ $t("keyMangage.check") }}</el-button
>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { linkEmits } from "element-plus";
import { getCurrentInstance } from "vue";

import { keyListApi } from "@/api_mangage/key";

import { useI18n } from "vue-i18n";

import { useRoute, useRouter } from "vue-router";

const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);

const tableData = ref([]);

const showDetail = (id) => {
router.push({
path: "/keyCheck",
query: {
id,
},
});
};

const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);

const getStatus = (val) => {
return val * 1 ? "失效" : "正常";
};

const handleSizeChange = (val) => {
pageSize.value = val;
getKeyListFunc(pageNum.value, pageSize.value);
};

const handleCurrentChange = (val) => {
pageNum.value = val;
getKeyListFunc(pageNum.value, pageSize.value);
};

const getKeyListFunc = async (PageNum, PageSize) => {
try {
const res = await keyListApi(PageNum, PageSize);
console.log(res, "res");
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};

onMounted(() => {
getKeyListFunc(1, 10);
});
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 75
- 289
src/views/login/index.vue ファイルの表示

@@ -31,7 +31,7 @@
</el-tooltip>
</div>

<el-form-item prop="email">
<el-form-item prop="username">
<div class="text-white">
<svg-icon icon-class="user" />
</div>
@@ -39,8 +39,8 @@
class="flex-1"
ref="email"
size="large"
v-model="loginData.email"
:placeholder="$t('login.email')"
v-model="loginData.username"
:placeholder="$t('login.username')"
name="email"
/>
</el-form-item>
@@ -67,14 +67,14 @@
</el-form-item>

<!-- 验证码 -->
<el-form-item prop="verifyCode">
<el-form-item prop="captcha">
<span class="text-white">
<svg-icon icon-class="verify_code" />
</span>
<el-input
v-model="loginData.verifyCode"
v-model="loginData.captcha"
auto-complete="off"
:placeholder="$t('login.verifyCode')"
:placeholder="$t('login.captcha')"
class="flex-1"
@keyup.enter="handleLogin"
/>
@@ -111,9 +111,6 @@
</el-button>

<div :class="currentLan == 'zh-cn' ? 'bottomBtnCN' : 'bottomBtnEN'">
<div style="margin-bottom: 5px" @click="loginFlag(2)">
<span class="join">{{ $t("login.signUp") }}</span>
</div>
<div style="margin-bottom: 5px">
<span class="join" @click="loginFlag(7)">{{
$t("login.SMSLogin")
@@ -126,188 +123,8 @@
</div>
</div>
</el-form>
<!-- 注册账户 isLogin == 2-->
<el-form
v-if="isLogin == 2"
ref="loginFormRef"
:model="loginData2"
:rules="loginRules"
class="login-form"
>
<div class="flex text-white items-center">
<span class="flex-1 text-center">{{
$t("login.title") + " " + $t("login.register")
}}</span>
<el-tooltip
class="box-item"
effect="dark"
:content="$t('navbar.LanguageChange')"
placement="top-start"
>
<lang-select class="navItem" style="color: #ffffff" />
</el-tooltip>
</div>

<el-form-item prop="email">
<div class="text-white">
<svg-icon icon-class="user" />
</div>
<el-input
class="flex-1"
ref="email"
size="large"
v-model="loginData2.email"
:placeholder="$t('login.email')"
name="email"
/>
</el-form-item>

<el-form-item prop="password">
<span class="text-white">
<svg-icon icon-class="password" />
</span>
<el-input
class="flex-1"
v-model="loginData2.password"
:placeholder="$t('login.password')"
:type="passwordVisible === false ? 'password' : 'input'"
size="large"
name="password"
/>
<span class="mr-2" @click="passwordVisible = !passwordVisible">
<svg-icon
:icon-class="passwordVisible === false ? 'eye' : 'eye-open'"
class="text-white cursor-pointer"
/>
</span>
</el-form-item>

<el-form-item prop="confirmPassword">
<span class="text-white">
<svg-icon icon-class="password" />
</span>
<el-input
class="flex-1"
v-model="loginData2.confirmPassword"
:placeholder="$t('login.confirmPassword')"
:type="passwordVisible === false ? 'password' : 'input'"
size="large"
name="confirmPassword"
@keyup.enter="handleRegister"
/>
<span class="mr-2" @click="passwordVisible = !passwordVisible">
<svg-icon
:icon-class="passwordVisible === false ? 'eye' : 'eye-open'"
class="text-white cursor-pointer"
/>
</span>
</el-form-item>

<el-button
size="default"
:loading="loading"
type="primary"
class="loginBtn"
@click.prevent="handleRegister"
>
<el-icon
v-if="isRegistering"
style="margin-right: 10px"
class="is-loading"
>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-ea893728=""
>
<path
fill="currentColor"
d="M512 64a32 32 0 0 1 32 32v192a32 32 0 0 1-64 0V96a32 32 0 0 1 32-32zm0 640a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V736a32 32 0 0 1 32-32zm448-192a32 32 0 0 1-32 32H736a32 32 0 1 1 0-64h192a32 32 0 0 1 32 32zm-640 0a32 32 0 0 1-32 32H96a32 32 0 0 1 0-64h192a32 32 0 0 1 32 32zM195.2 195.2a32 32 0 0 1 45.248 0L376.32 331.008a32 32 0 0 1-45.248 45.248L195.2 240.448a32 32 0 0 1 0-45.248zm452.544 452.544a32 32 0 0 1 45.248 0L828.8 783.552a32 32 0 0 1-45.248 45.248L647.744 692.992a32 32 0 0 1 0-45.248zM828.8 195.264a32 32 0 0 1 0 45.184L692.992 376.32a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0zm-452.544 452.48a32 32 0 0 1 0 45.248L240.448 828.8a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0z"
></path>
</svg>
</el-icon>
{{ $t("login.registerBtn") }}
</el-button>

<div :class="currentLan == 'zh-cn' ? 'bottomBtnCN' : 'bottomBtnEN'">
<div style="margin-bottom: 5px" @click="loginFlag(1)">
<span class="join">{{ $t("login.signIn") }}</span>
</div>
<div>
<span class="join" @click="loginFlag(3)">{{
$t("login.forgetPwd")
}}</span>
</div>
</div>
</el-form>
<!-- 注册账户之后输入短信验证码 isLogin == 6-->
<el-form
v-if="isLogin == 6"
ref="loginFormRef"
:model="loginData6"
:rules="loginRules"
class="login-form"
>
<div class="flex text-white items-center">
<span class="flex-1 text-center">{{
$t("login.title") + " " + $t("login.register")
}}</span>
<el-tooltip
class="box-item"
effect="dark"
:content="$t('navbar.LanguageChange')"
placement="top-start"
>
<lang-select class="navItem" style="color: #ffffff" />
</el-tooltip>
</div>

<el-form-item prop="SMSCode">
<div class="text-white">
<svg-icon icon-class="user" />
</div>
<el-input
class="flex-1"
ref="email"
size="large"
v-model="loginData6.SMSCode"
:placeholder="$t('login.SMSCode')"
name="email"
/>
</el-form-item>

<el-button
size="default"
:loading="loading"
type="primary"
class="loginBtn"
@click.prevent="handleRegister"
>
<el-icon
v-if="isRegistering"
style="margin-right: 10px"
class="is-loading"
>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-ea893728=""
>
<path
fill="currentColor"
d="M512 64a32 32 0 0 1 32 32v192a32 32 0 0 1-64 0V96a32 32 0 0 1 32-32zm0 640a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V736a32 32 0 0 1 32-32zm448-192a32 32 0 0 1-32 32H736a32 32 0 1 1 0-64h192a32 32 0 0 1 32 32zm-640 0a32 32 0 0 1-32 32H96a32 32 0 0 1 0-64h192a32 32 0 0 1 32 32zM195.2 195.2a32 32 0 0 1 45.248 0L376.32 331.008a32 32 0 0 1-45.248 45.248L195.2 240.448a32 32 0 0 1 0-45.248zm452.544 452.544a32 32 0 0 1 45.248 0L828.8 783.552a32 32 0 0 1-45.248 45.248L647.744 692.992a32 32 0 0 1 0-45.248zM828.8 195.264a32 32 0 0 1 0 45.184L692.992 376.32a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0zm-452.544 452.48a32 32 0 0 1 0 45.248L240.448 828.8a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0z"
></path>
</svg>
</el-icon>
{{ $t("login.registerBtn") }}
</el-button>

<div :class="currentLan == 'zh-cn' ? 'bottomBtnCN' : 'bottomBtnEN'">
<div style="margin-bottom: 5px" @click="loginFlag(1)">
<span class="join">{{ $t("login.signIn") }}</span>
</div>
</div>
</el-form>
<!-- 忘记密码 isLogin == 3-->
<el-form
v-if="isLogin == 3"
@@ -691,9 +508,6 @@
</el-button>

<div :class="currentLan == 'zh-cn' ? 'bottomBtnCN' : 'bottomBtnEN'">
<div style="margin-bottom: 5px" @click="loginFlag(2)">
<span class="join">{{ $t("login.signUp") }}</span>
</div>
<div style="margin-bottom: 5px">
<span class="join" @click="loginFlag(1)">{{
$t("login.pwsLogin")
@@ -786,8 +600,7 @@
import LangSelect from "@/components/LangSelect/index.vue";
import SvgIcon from "@/components/SvgIcon/index.vue";
import {
register,
login,
loginApi,
sendRegisterEmail,
getCodeImgUrl,
sendUpdPwdEmailApi,
@@ -799,6 +612,7 @@ import {
doFindInviteCode,
doUpdateInviteCode,
} from "@/apis/login";
import { getMenuNavApi } from "@/apis/Permission.js";
import axios from "axios";
// 状态管理依赖

@@ -848,9 +662,9 @@ const isLogining = ref(false);
const isRegistering = ref(false);

const loginData = ref({
email: "",
username: "",
password: "",
verifyCode: "",
captcha: "",
});

const loginData2 = ref({
@@ -882,6 +696,13 @@ const loginData8 = ref({
invitatioCode: "",
});
const loginRules = ref({
username: [
{
required: true,
trigger: "blur",
validator: passwordValidator,
},
],
email: [
{
required: true,
@@ -894,13 +715,13 @@ const loginRules = ref({
confirmPassword: [
{ required: true, trigger: "blur", validator: confirmPasswordValidator },
],
verifyCode: [
captcha: [
{
required: true,
trigger: "blur",
message:
currentLan.value == "zh-cn"
? "请输入验证码"
? "请输入验证码2"
: "Please input verifyCode",
},
],
@@ -928,8 +749,8 @@ const loginRules = ref({

watch(currentLan, (newValue, oldValue) => {
const loginRule = loginRules.value;
loginRule.verifyCode[0].message =
newValue == "zh-cn" ? "请输入验证码" : "Please input verifyCode";
loginRule.captcha[0].message =
newValue == "zh-cn" ? "请输入验证码1" : "Please input verifyCode";
setTimeout(() => {
loginFormRef.value.resetFields();
getInfo();
@@ -973,7 +794,7 @@ function getInfo() {
const deCodeUserInfo = decodeURI(data as any);
const userInfo = JSON.parse(deCodeUserInfo);
if (userInfo) {
loginData.value.email = userInfo.email ? userInfo.email : "";
loginData.value.username = userInfo.email ? userInfo.email : "";
loginData.value.password = userInfo.password ? userInfo.password : "";
}
}
@@ -1091,48 +912,7 @@ function confirmPasswordValidator5(rule: any, value: any, callback: any) {
}
//#endregion ----------------------------

// 登录行为
function handleLogin() {
loginFormRef.value.validate((val: any) => {
if (val) {
const data = {
phone: loginData.value.email,
password: loginData.value.password,
code: loginData.value.verifyCode,
};
isLogining.value = true;
doLogin(data);
} else {
const msg =
currentLan.value == "zh-cn"
? "请检查信息格式!"
: "Please check the format !";
ElMessage.error(msg);
return;
}
});
}

// 注册行为
function handleRegister() {
loginFormRef.value.validate((val: any) => {
if (val) {
const data = {
phone: loginData2.value.email,
password: loginData2.value.password,
};
isRegistering.value = true;
doRegister(data);
} else {
const msg =
currentLan.value == "zh-cn"
? "请检查信息格式!"
: "Please check the format !";
ElMessage.error(msg);
return;
}
});
}

//#region 忘记密码 表单3
function sendEmailforgetPwd() {
@@ -1178,7 +958,7 @@ function resetPwdEmail() {
ticket: route.query.ticket,
});
localStorage.removeItem("AccessToken");
loginData.value.email = route.query.email + "";
// loginData.value.email = route.query.email + "";
loginData.value.password = "";
isLogin.value = 1;
router.push({ path: "login", query: {} });
@@ -1329,7 +1109,7 @@ async function checkInviteCode() {
isLogin.value = 8;
// 有邀请码直接前往我的页面
} else if (status == 1) {
userInfoPinia.getUserInfo(); //保存邮箱到pinia
// userInfoPinia.getUserInfo(); //保存邮箱到pinia
ElMessage.success(
'currentLan.value == "zh-cn" ? "登录成功!" : "Welcome to Metavatar!"'
);
@@ -1351,7 +1131,7 @@ async function handleInvitatioCode() {
};
try {
const res = await doUpdateInviteCode(data);
userInfoPinia.getUserInfo(); //保存邮箱到pinia
// userInfoPinia.getUserInfo(); //保存邮箱到pinia
ElMessage.success(
'currentLan.value == "zh-cn" ? "登录成功!" : "Welcome to Metavatar!"'
);
@@ -1370,92 +1150,98 @@ function getCode() {
axios({
url: getCodeImgUrl,
method: "get",
}).then(() => {
}).then((res) => {
verifyCodeUrl.value = getCodeImgUrl;
showCodeImg.value = true;
});
}

/**
* @description:注册
* @description:登录
* @param {Object} data
* @return: data
*/
async function doRegister(data: object) {

/**
* @description:重新发送邮件
* @param {string} email
* @return: data
*/
async function reSendEmail(email: string) {
const msg =
currentLan.value == "zh-cn" ? "注册成功!" : "Registered successfully";
await register(data)
currentLan.value == "zh-cn"
? "确认信息已发送至您的邮箱,请注意查收!"
: "An email has been send to you, Please pay attention to confirm !";
await sendRegisterEmail(email)
.then((res) => {
console.log(res, "res");
isRegistering.value = false;
ElMessage.success(msg);
isLogin.value = 1;
// 注册成功将账号密码保存到本地
saveInfo(data);
router.go(0);
})
.catch((err) => {
// 注册失败刷新验证码
getCode();
isRegistering.value = false;
console.log(err, "err");
ElMessage.error(err.message);
});
}

/**
* @description:登录
* @param {Object} data
* @return: data
*/
//#region 新登录
// 登录行为
function handleLogin() {
loginFormRef.value.validate((val: any) => {
if (val) {
const data = {
username: loginData.value.username,
password: loginData.value.password,
captcha: loginData.value.captcha,
};
isLogining.value = true;
doLogin(data);
} else {
const msg =
currentLan.value == "zh-cn"
? "请检查信息格式!"
: "Please check the format !";
ElMessage.error(msg);
return;
}
});
}
async function doLogin(data: any) {
const msg =
currentLan.value == "zh-cn" ? "登录成功!" : "Welcome to Metavatar!";
await login(data)
await loginApi(data)
.then(async (res) => {
console.log(res, "res");
localStorage.setItem("AccessToken", res.data.token);
// localStorage.setItem("AccessToken", res.data.token);
// 注册成功将账号密码保存到本地
saveInfo(data);
await getMenuNavFunc();
isLogining.value = false;
// await checkInviteCode();
userInfoPinia.getUserInfo(); //保存邮箱到pinia
// userInfoPinia.getUserInfo(); //保存邮箱到pinia
ElMessage.success(msg);
router.push("/createVideo");
router.push("/home");
})
.catch((err) => {
console.log(err, "err");
// 登录失败刷新验证码
getCode();
ElMessage.error(err.message);
ElMessage.error(err);
isLogining.value = false;
if (err.code == 2112) {
reSendEmail(data.email);
}
});
}
//#endregion

/**
* @description:重新发送邮件
* @param {string} email
* @return: data
*/
async function reSendEmail(email: string) {
const msg =
currentLan.value == "zh-cn"
? "确认信息已发送至您的邮箱,请注意查收!"
: "An email has been send to you, Please pay attention to confirm !";
await sendRegisterEmail(email)
.then((res) => {
console.log(res, "res");
ElMessage.success(msg);
})
.catch((err) => {
console.log(err, "err");
ElMessage.error(err.message);
});
//#region 获取菜单导航路由
async function getMenuNavFunc() {
try {
const res = await getMenuNavApi();
} catch (error) {
console.log(error);
}
}
//#endregion --------------------------------
onMounted(() => {
getCode();
getInfo();


+ 16
- 0
src/views/mangage/model.vue ファイルの表示

@@ -0,0 +1,16 @@
<template>
<div class="page flex justify-around"></div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup></script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #fff;
border-radius: 30px 0 0 0;
overflow: hidden;
}
</style>

+ 16
- 0
src/views/mangage/ruleList.vue ファイルの表示

@@ -0,0 +1,16 @@
<template>
<div class="page flex justify-around"></div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup></script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #fff;
border-radius: 30px 0 0 0;
overflow: hidden;
}
</style>

+ 16
- 0
src/views/mangage/userList.vue ファイルの表示

@@ -0,0 +1,16 @@
<template>
<div class="page flex justify-around"></div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup></script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #fff;
border-radius: 30px 0 0 0;
overflow: hidden;
}
</style>

+ 0
- 698
src/views/myCreating/index copy.vue ファイルの表示

@@ -1,698 +0,0 @@
<template>
<div class="page">
<!-- 搜索框 -->
<div class="top">
<div class="flex justify-between" style="width: 300px">
<el-input
v-model="title"
:placeholder="$t('myCreating.search')"
@keyup.enter="searchVideoList"
/>
<el-icon class="icon-edit" @click="searchVideoList"
><i-ep-search
/></el-icon>
</div>
</div>
<!-- 作品列表 -->
<div v-if="!isLoading">
<div v-if="videoList.length > 0" class="workList flex">
<div class="workItem" v-for="item in videoList" :key="item.id">
<div class="workImg">
<img
v-if="item.coverImg"
class="coverImg inmiddle"
:src="item.coverImg"
alt=""
/>
<img
v-else
class="takePlace inmiddle"
src="@/assets/img/left-top-logo.png"
alt=""
/>
</div>

<div :class="'statusName ' + item.statusName">
{{ $t("myCreating." + item.statusName) }}
</div>

<div class="workInfo flex">
<span>{{ item.title }}</span>
<span>{{ dayjs(item.createDate).format("YYYY-MM-DD") }}</span>
</div>
<!-- 更多遮罩层 -->
<div
class="more"
@mouseleave="mouseleave"
@click="clickVideoItem(item)"
>
<el-icon
v-if="[0].includes(item.videoStatus)"
class="icon-videoPlay"
><i-ep-edit
/></el-icon>
<el-icon
v-if="[1, 2, 4, 6].includes(item.videoStatus)"
class="icon-videoPlay"
><i-ep-loading
/></el-icon>
<el-icon
v-if="[3].includes(item.videoStatus)"
class="icon-videoPlay"
><i-ep-closeBold
/></el-icon>
<el-icon
v-if="[5].includes(item.videoStatus)"
class="icon-videoPlay"
><i-ep-videoPlay
/></el-icon>
<div class="showBtn" @click.stop="mouseenter(item)">...</div>
<div class="btns" v-show="showPopup">
<!-- 下载按钮 -->
<div
class="btns-item"
@click.stop="handleDownload(item.videoPlayUrl)"
>
<div class="btns-item-icon">
<el-icon class="icon-delete"><i-ep-download /></el-icon>
</div>
<div class="text">{{ $t("myCreating.download") }}</div>
</div>
<!-- 删除按钮 -->
<div
class="btns-item"
@click.stop="handelDelete(item.id, item.title)"
>
<div class="btns-item-icon">
<el-icon class="icon-delete"><i-ep-delete /></el-icon>
</div>
<div class="text">{{ $t("myCreating.delete") }}</div>
</div>
</div>
</div>
</div>
</div>
<div v-if="videoList.length == 0" class="noList">
<div class="title">
{{ $t("myCreating.noList") }}
</div>
<div class="goCreate" @click="goCreate">
{{ $t("myCreating.goCreate") }}
</div>
</div>
</div>
<div v-if="isLoading" class="loadingIcon">
<el-icon class="is-loading">
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-ea893728=""
>
<path
fill="currentColor"
d="M512 64a32 32 0 0 1 32 32v192a32 32 0 0 1-64 0V96a32 32 0 0 1 32-32zm0 640a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V736a32 32 0 0 1 32-32zm448-192a32 32 0 0 1-32 32H736a32 32 0 1 1 0-64h192a32 32 0 0 1 32 32zm-640 0a32 32 0 0 1-32 32H96a32 32 0 0 1 0-64h192a32 32 0 0 1 32 32zM195.2 195.2a32 32 0 0 1 45.248 0L376.32 331.008a32 32 0 0 1-45.248 45.248L195.2 240.448a32 32 0 0 1 0-45.248zm452.544 452.544a32 32 0 0 1 45.248 0L828.8 783.552a32 32 0 0 1-45.248 45.248L647.744 692.992a32 32 0 0 1 0-45.248zM828.8 195.264a32 32 0 0 1 0 45.184L692.992 376.32a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0zm-452.544 452.48a32 32 0 0 1 0 45.248L240.448 828.8a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0z"
></path>
</svg>
</el-icon>
<span class="loadingText">{{ $t("myCreating.loading") }}</span>
</div>
<!-- 弹出框预览作品 -->
<el-dialog
destroy-on-close
class="elDialog"
v-model="showDialog"
:title="presentItem.title"
width="40%"
center
>
<div class="videoBox">
<video
v-if="presentItem.videoPlayUrl"
controls="true"
class=""
:src="presentItem.videoPlayUrl"
></video>
<div v-else class="insteadVideo">
{{ $t("myCreating." + presentItem.statusName + "Text") }}
<span v-if="presentItem.videoStatus == 3">{{
presentItem.videoMsg
}}</span>
</div>
<!-- 有视频路径的页脚 -->
<div class="footer" v-if="presentItem.videoPlayUrl">
<span class="dialog-footer">
<!-- dialog下载按钮 -->
<button
class="elBtn"
type="primary"
@click.stop="handleDownload(presentItem.videoPlayUrl)"
>
<el-icon class="icon-delete"><i-ep-download /></el-icon
>{{ $t("myCreating.download") }}
</button>
<!-- dialog删除按钮 -->
<button
class="elBtn"
@click.stop="handelDelete(presentItem.id, presentItem.title)"
>
<el-icon class="icon-delete"><i-ep-delete /></el-icon
>{{ $t("myCreating.delete") }}
</button>
</span>
</div>
<!-- 无视频路径的页脚 -->
<div class="footer" v-if="!presentItem.videoPlayUrl">
<span class="dialog-footer">
<!-- dialog编辑按钮 -->
<button
class="elBtn"
type="primary"
@click.stop="
router.push({
path: '/createVideo',
query: { id: presentItem.id },
})
"
v-if="[0, 3].includes(presentItem.videoStatus)"
>
{{ $t("myCreating.edit") }}
</button>
</span>
</div>
</div>
</el-dialog>
<!-- 分页 -->
<div v-if="!isLoading" class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100, 200]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>

<script setup>
import {
userPhotoVideoListApi,
delUserPhotoVideoByIDApi,
} from "@/apis/myCreating";
import { timestampToTime } from "@/utils/tools";
import { useI18n } from "vue-i18n";
import { getStatus } from "./videoStatus";
import { useRouter } from "vue-router";
import dayjs from "dayjs";
const router = useRouter();
// 当前语言状态与国际化组件
const { locale } = useI18n();
const lanChange = ref(locale);

const title = ref("");
const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);
// 弹窗显示隐藏
const showDialog = ref(false);
const isLoading = ref(true);
// 当前作品
const presentItem = ref("");
// 作品列表
const videoList = ref([]);

const handleSizeChange = (val) => {
pageSize.value = val;
getPhotoVideoList(pageNum.value, pageSize.value, title.value);
};

const handleCurrentChange = (val) => {
pageNum.value = val;
getPhotoVideoList(pageNum.value, pageSize.value, title.value);
};

// 点击作品
function clickVideoItem(item) {
showDialog.value = true;
presentItem.value = item;
}

// 删除框是否显示
let showPopup = ref(false);
function mouseleave() {
showPopup.value = false;
}

function mouseenter(item) {
presentItem.value = item;
showPopup.value = !showPopup.value;
}
// 下载
function handleDownload(itemVideoPlayUrl) {
if (itemVideoPlayUrl) {
const link = document.createElement("a");
link.href = itemVideoPlayUrl;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else {
ElMessage.error(
lanChange.value == "zh-cn" ? `无法下载` : `Unable to download`
);
}
}

function goCreate() {
router.push("/createVideo");
}

function searchVideoList() {
getPhotoVideoList(pageNum.value, pageSize.value, title.value, true);
}

function handelDelete(id, title) {
const msg =
lanChange.value == "zh-cn"
? `确定要删除这个作品吗? 作品标题:${title}`
: `Are you sure to delete this video ? Title:${title};`;
const notice = lanChange.value == "zh-cn" ? "提示" : "Notice";
const confirm = lanChange.value == "zh-cn" ? "确定" : "Confirm";
const cancel = lanChange.value == "zh-cn" ? "取消" : "Cancel";
ElMessageBox.confirm(msg, notice, {
confirmButtonText: confirm,
cancelButtonText: cancel,
}).then(() => {
delPhotoVideo(id);
});
}

/**
* @description:获取我的视频作品
* @return: data
*/
function getPhotoVideoList(pageNum, pageSize, title, searchFlag) {
userPhotoVideoList(pageNum, pageSize, title)
.then((res) => {
console.log(res, "res");
res.data.list.forEach((item) => {
// item.createDate = timestampToTime(item.createDate) || "";
// item.updateDate = timestampToTime(item.updateDate) || "";
// item.createVideoDate = timestampToTime(item.createVideoDate) || "";
item.statusName = getStatus(item.videoStatus);
});
videoList.value = res.data.list;
total.value = res.data.total * 1;
if (searchFlag) {
const total = res.data.total;
const msg1 =
lanChange.value == "zh-cn"
? `查询成功!找到${total}条符合条件的视频`
: `Found! Match ${total} videos`;
const msg2 =
lanChange.value == "zh-cn"
? `查询成功!没有符合条件的视频`
: `Found! No match videos`;
const msg3 =
lanChange.value == "zh-cn"
? `查询成功!已加载全部视频`
: `Found! Present all videos`;
return title
? ElMessage.success(total > 0 ? msg1 : msg2)
: ElMessage.success(msg3);
}
isLoading.value = false;
})
.catch((err) => {
console.log(err, "err");
});
}

/**
* @description:删除视频作品
* @param: videoID
* @return: data
*/
function delPhotoVideo(videoID) {
delUserPhotoVideoByID(videoID)
.then((res) => {
console.log(res, "res");
const msg = lanChange.value == "zh-cn" ? `删除成功!` : `Success !`;
ElMessage.success(msg);
showDialog.value = false;
getPhotoVideoList(pageNum.value, pageSize.value, title.value);
})
.catch((err) => {
console.log(err, "err");
ElMessage.error(err.message);
});
}

onMounted(() => {
isLoading.value = true;
getPhotoVideoList(pageNum.value, pageSize.value, title.value);
});
</script>

<style lang="scss" scoped>
.page {
min-height: calc(100vh - 80px);
padding: 15px 130px;
background-color: #fff;
border-radius: 25px 0 0;
.top {
margin-bottom: 35px;
}
.loadingIcon {
height: 100px;
line-height: 100px;
text-align: center;
transform: scale(2);
.loadingText {
position: relative;
top: -2px;
left: 4px;
}
}
.workList {
flex-wrap: wrap;
justify-content: flex-start;
width: 100%;
// height: 200px;
margin-top: 5px;
padding-bottom: 50px;
cursor: pointer;
.workItem {
position: relative;
width: 300px;
// height: 200px;
margin: 0 20px 20px 0;
border: 2px solid #c2c2c2;
border-radius: 15px;
cursor: pointer;
overflow: hidden;
.workImg {
position: relative;
width: 100%;
height: 170px;
cursor: pointer;
padding: 10px;
overflow: hidden;
.coverImg {
max-width: 90%;
max-height: 170px;
cursor: pointer;
}
.takePlace {
position: absolute;
top: 40%;
width: 80%;
}
}

.more {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 170px;
background-color: rgb(0 0 0 / 30%);
cursor: pointer;
opacity: 0;
transition: all 0.2s;
.icon-videoPlay {
position: absolute;
top: 50%;
left: 50%;
width: 50px;
height: 50px;
font-size: 50px;
color: #fff;
text-align: center;
transform: translate(-50%, -50%);
cursor: pointer;
}

.showBtn {
position: absolute;
top: 10px;
right: 10px;
width: 20px;
height: 20px;
font-size: 20px;
line-height: 10px;
color: #fff;
text-align: center;
transition: all 0.3s;
cursor: pointer;
border-radius: 2px;
&:hover {
background-color: #00000056;
}
}

.btns {
position: absolute;
top: 45px;
right: 10px;
// width: 70px;
// height: 60px;
overflow: hidden;
border-radius: 10px;
cursor: pointer;

&-item {
// width: 70px;
// height: 30px;
text-align: center;
background-color: #fff;
padding: 0 10px;
transition: all 0.3s;
cursor: pointer;
&-icon {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
cursor: pointer;
.icon-delete {
position: absolute;
top: 4px;
left: 0;
width: 20px;
height: 20px;
font-size: 16px;
color: #000;
text-align: center;
cursor: pointer;
}
}

.text {
display: inline-block;
height: 30px;
padding-left: 5px;
margin-top: -5px;
line-height: 20px;
cursor: pointer;
}
&:hover {
color: #ffffff;
background-color: #0000001a;
}
}
}
}

.workInfo {
justify-content: space-between;
height: 40px;
padding: 0 10px;
line-height: 40px;
border-top: 1px solid #afafaf;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 13px;
}
.statusName {
position: absolute;
top: 10%;
left: -10%;
width: 120px;
text-align: center;
color: #ffffff;
font-size: 14px;
transform: rotate(-45deg);

&.manuscript {
/* 草稿 */
background: linear-gradient(to right, #d7d2cc 0%, #304352 100%);
}
&.generating {
/* 生成中 */
background: linear-gradient(270deg, #4b61dc, #15d1b8);
}
&.generationFailed {
/* 生成失败 */
background: linear-gradient(120deg, #f093fb 0%, #f5576c 100%);
}
&.generationSucceed {
/* 生成成功 */
background: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%);
}
}
&:hover {
.more {
opacity: 1;
}
}
}
}
.noList {
.title {
font-size: 30px;
text-align: center;
margin-top: 100px;
background-image: -webkit-linear-gradient(
left,
#4b61dc,
#15d1b8 25%,
#4b61dc 50%,
#15d1b8 75%,
#4b61dc
) !important;
-webkit-text-fill-color: transparent !important;
-webkit-background-clip: text !important;
background-clip: text !important;
-webkit-background-size: 200% 100% !important;
background-size: 200% 100% !important;
}

.goCreate {
width: 200px;
height: 40px;
line-height: 40px;
text-align: center;
color: #ffffff;
border-radius: 6px;
background: linear-gradient(270deg, #4b61dc, #15d1b8);
margin: 30px auto;
cursor: pointer;
transition: all 0.3s;
&:hover {
transform: scale(1.1);
}
}
}
.pagination {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background-color: #ffffff;
padding: 10px 20px;
border-radius: 8px;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}

:deep(.el-input__wrapper) {
border: none;
border-bottom: 2px solid #000;
border-radius: 0;
box-shadow: none;
}

.el-icon.icon-edit {
width: 30px;
height: 30px;
font-size: 30px;
color: #8c939d;
text-align: center;
cursor: pointer;
transition: all 0.3s;
&:hover {
color: #292929;
}
}

.elDialog {
.videoBox {
// display: flex;
// justify-content: space-between;
width: 100%;
padding: 20px;
video {
width: 100%;
max-height: 600px;
margin: auto;
}
.insteadVideo {
font-size: 20px;
font-weight: 600;
margin: 20px auto;
text-align: center;
}
}

.dialog-footer {
display: flex;
justify-content: space-between;
.elBtn {
color: #fff;
border-radius: 10px;
padding: 5px 10px;
cursor: pointer;
background-color: #000;
transition: all 0.3s;
&:hover {
background-color: #15d1b8;
}
}

:deep(.el-icon) {
margin-right: 5px;
font-weight: 700;
}
}
}

:deep(.el-dialog) {
overflow: hidden;
border-radius: 25px;
}
// :deep(.el-dialog__header) {
// }
:deep(.el-dialog__title) {
background-image: -webkit-linear-gradient(
left,
#4b61dc,
#15d1b8 25%,
#4b61dc 50%,
#15d1b8 75%,
#4b61dc
) !important;
-webkit-text-fill-color: transparent !important;
-webkit-background-clip: text !important;
background-clip: text !important;
-webkit-background-size: 200% 100% !important;
background-size: 200% 100% !important;
}

:deep(.el-dialog__body) {
padding: 0;
}

:deep(.dialog__footer) {
padding: 0;
}
</style>

+ 378
- 0
src/views/privatization/index.vue ファイルの表示

@@ -0,0 +1,378 @@
<template>
<div class="page justify-around">
<div class="addbtn">
<el-button type="primary" @click="dialogVisible = true"><el-icon><i-ep-plus /></el-icon> 新增</el-button>
</div>

<el-table
:data="tableData"
style="width: 100%; height: 90%"
size="large"
align="center"
border
>
<el-table-column
prop="name"
:label="$t('businessMangage.name')"
align="center"
/>

<el-table-column
prop="address"
:label="$t('businessMangage.address')"
align="center"
/>

<el-table-column
prop="status"
:label="$t('businessMangage.status')"
align="center"
>
<template #default="scope">
<span
:style="1 * scope.row.status ? 'color: #ff0000' : 'color: #36db43'"
>{{ getStatus(scope.row.status) }}</span
>
</template>
</el-table-column>

<el-table-column
prop="code"
:label="$t('businessMangage.code')"
align="center"
/>

<el-table-column
prop="createTime"
:label="$t('businessMangage.createTime')"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column
prop="createTime"
label="时长(秒)"
align="center"
>
<template #default="scope">
{{ scope.row.remainingTimes }}
</template>
</el-table-column>
<el-table-column
fixed="right"
:label="$t('keyMangage.operations')"
width="400"
align="center"
>
<template #default="scope">
<el-button
link
type="primary"
size="small"
@click="showDetail(scope.row.id)"
>查看密钥</el-button>
<el-button link type="primary" size="small" @click="setTime(scope.row.id)">设置时长</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog
v-model="dialogVisible"
:title="$t('businessMangage.addNew')"
width="35%"
:before-close="handleClose"
>
<div class="formData">
<el-form
ref="ruleForm"
label-position="left"
label-width="120px"
:model="formData"
:rules="rules"
>
<el-form-item :label="$t('businessMangage.name') + ':'" prop="name">
<el-input
v-model="formData.name"
:placeholder="$t('businessMangage.nameInputText')"
/>
</el-form-item>

<el-form-item
:label="$t('businessMangage.address') + ':'"
prop="address"
>
<el-input
v-model="formData.address"
:placeholder="$t('businessMangage.addressInputText')"
/>
</el-form-item>

<el-form-item :label="$t('businessMangage.code') + ':'" prop="code">
<el-input
v-model="formData.code"
:placeholder="$t('businessMangage.codeInputText')"
/>
</el-form-item>

<el-form-item
:label="$t('businessMangage.accessMethod') + ':'"
prop="type"
>
<el-select
v-model="formData.type"
:placeholder="$t('businessMangage.typeInputText')"
clearable
disabled
>
<el-option
v-for="(item, index) in accessMethods"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">{{
$t("businessMangage.cancel")
}}</el-button>
<el-button type="primary" @click="addNew(ruleForm)">
{{ $t("businessMangage.confirm") }}
</el-button>
</span>
</template>
</el-dialog>

<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { businessListApi, addBusinessApi, setRemainingTimes, saveMallUserInfo } from '@/apis/apiManage.js'

import { useI18n } from "vue-i18n";

import dayjs from "dayjs";

import { useRoute, useRouter } from "vue-router";

const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);

const tableData = ref([]);

const dialogVisible = ref(false);

const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);

const getStatus = (val) => {
return val * 1 ? "锁定" : "正常";
};

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD");
};

const handleSizeChange = (val) => {
pageSize.value = val;
getListData(pageNum.value, pageSize.value);
};

const handleCurrentChange = (val) => {
pageNum.value = val;
getListData(pageNum.value, pageSize.value);
};

const getListData = async (PageNum, PageSize) => {
try {
const res = await businessListApi(PageNum, PageSize, 2);
console.log(res, "res");
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};

const formData = ref({
name: "",
address: "",
code: "",
type: 2,
});

const rules = {
name: [
{
required: true,
message: "Please input name",
trigger: "blur",
},
],
address: [
{
required: true,
message: "Please input address",
trigger: "blur",
},
],
code: [
{
required: true,
message: "Please input code",
trigger: "blur",
},
],
type: [
{
required: true,
message: "Please select access methods",
trigger: "blur",
},
],
};

const accessMethods = ref([
{
label: "api接入",
value: 1,
},
{
label: "私有化接入",
value: 2,
},
]);

const handleClose = () => {
dialogVisible.value = false;
setTimeout(() => {
formData.value = {
name: "",
address: "",
code: "",
type: 1,
};
}, 500);
};

const ruleForm = ref();

const addNew = async (refForm) => {
if (!refForm) return;
await refForm.validate(async (valid, fields) => {
if (valid) {
const data = formData.value;
try {
const res = await addBusinessApi(data);
console.log(res, "res");
if (res.code == 200) {
ElMessage.success("添加成功!");
dialogVisible.value = false;
}
getListData(1, 10);
pageNum.value = 1;
pageSize.value = 10;
} catch (error) {
console.log(error, "err");
ElMessage.error(error.message);
}
} else {
ElMessage.error("请完善信息!");
}
});
};

// 查看时长
const showDetail = (id) => {
router.push({
path: "/businessCheck",
query: {
id,
},
});
}

// 设置时长
const setTime = (id) => {
ElMessageBox.prompt('请输入时长(秒)', '设置时长', {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
inputPattern: /^[0-9]*$/,
inputErrorMessage: '请输入数字',
beforeClose: async (action, instance, done) => {
if (action == "confirm") {//点击确认请求接口
let res = await setRemainingTimes({ id: id, times: instance.inputValue })
if (res.code === 200) {
// 通过监测关闭
ElMessage.success('设置成功')
done();
} else {
ElMessage.error(res.message)
}
} else {
//点击取消关闭
done();
}
}
})
}

onMounted(() => {
getListData(1, 10);
});
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}

.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 43
- 0
src/views/secretKey/index.vue ファイルの表示

@@ -0,0 +1,43 @@
<template>
<el-form :model="form" label-width="120px" class="page">
<el-form-item label="name">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="appId">
<el-input v-model="form.appId" />
</el-form-item>
<el-form-item label="appKey">
<el-input v-model="form.appKey" />
</el-form-item>
<el-form-item label="signKey">
<el-input v-model="form.signKey" />
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { reactive } from 'vue'
import { secretKeyApi } from '@/apis/secretKey.js'
const form = ref({
name: '',
appId: '',
appKey: '',
signKey: ''
})
function getSecretKey() {
secretKeyApi().then(res => {
form.value = res.data
})
}
onMounted(() => {
getSecretKey();
});
</script>
<style lang="scss" scoped>
.page{
height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
}
</style>

+ 124
- 0
src/views/template/index.vue ファイルの表示

@@ -0,0 +1,124 @@
<template>
<div class="page justify-around">

<el-table
:data="tableData"
style="width: 100%; height: 90%"
size="large"
align="center"
border
>

<el-table-column :label="$t('template.img')" align="center">
<template #default="scope">
<img :src="scope.row.coverImg" width="50" />
</template>
</el-table-column>
<el-table-column
prop="createTime"
label="创建时间"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createTime) }}
</template>
</el-table-column>
</el-table>

<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { templateListApi } from '@/apis/template.js'

import { useI18n } from "vue-i18n";

import dayjs from "dayjs";

import { useRoute, useRouter } from "vue-router";

const route = useRoute();
const router = useRouter();

const tableData = ref([]);

const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD");
};

const handleSizeChange = (val) => {
pageSize.value = val;
getListData(pageNum.value, pageSize.value);
};

const handleCurrentChange = (val) => {
pageNum.value = val;
getListData(pageNum.value, pageSize.value);
};

const getListData = async (PageNum, PageSize) => {
try {
const res = await templateListApi(PageNum, PageSize);
console.log(res, "res");
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};
onMounted(() => {
getListData(1, 10);
});
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}

.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 91
- 0
src/views/userManage/personMould.vue ファイルの表示

@@ -0,0 +1,91 @@
<template>
<div>
<el-checkbox-group v-model="checkboxGroup" size="small" class="checkbox">
<el-checkbox v-for="(item, index) in tableData" :key="index" :label="item.id" border>
<img :src="item.coverImg" width="50" />
<p>{{ item.title }}</p>
</el-checkbox>
</el-checkbox-group>
<div style="margin-top: 20px">
<el-button @click="handleClose"
>{{$t('personMould.cancel')}}</el-button
>
<el-button type="primary" @click="addModel()">{{$t('personMould.confirm')}}</el-button>
</div>
</div>
</template>

<script setup>
import { ref } from 'vue'
import { personMouldListApi, userPersonMouldIdListApi, associatedUserMouldsApi } from '@/apis/userManage.js'
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
const { locale } = useI18n();
const lanChange = ref(locale);

const emit = defineEmits(["close"]);
const props = defineProps({
cUserId: {
required: true,
}
});

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD");
};

const checkboxGroup = ref([])
const total = ref(0);
const tableData = ref([]);
const modelAllListFunc = async () => {
try {
const res = await personMouldListApi(1, 10000);
const modelIds = await userPersonMouldIdListApi(props.cUserId);
tableData.value = res.data.list;
total.value = res.data.total * 1;
setTimeout(()=> {
if(modelIds.data && modelIds.data.length > 0) {
modelIds.data.forEach((item) => {
checkboxGroup.value.push(item)
let set = new Set(checkboxGroup.value)
checkboxGroup.value = Array.from(set)
})
}
}, 500)

} catch (error) {
console.log(error, "err");
}
};

const addModel = async () => {
const data = {
mouldIds: checkboxGroup.value,
userId: props.cUserId
}
const res = await associatedUserMouldsApi(data);
if(res.code === 200) {
ElMessage.success((lanChange.value) == "zh-cn" ? "关联成功!" : 'Successfully associated!');
handleClose()
}
};

function handleClose() {
emit("close");
}
onMounted(() => {
modelAllListFunc(1, 10);
});
</script>

<style lang="scss" scoped>
.checkbox {
max-height: 500px;
overflow: auto;
:deep .el-checkbox.el-checkbox--small {
width: 110px;
height:110px;
margin-bottom: 10px;
}
}
</style>

+ 146
- 0
src/views/userManage/personVoice.vue ファイルの表示

@@ -0,0 +1,146 @@
<template>
<el-table
ref="multipleTableRef"
:data="tableData"
style="width: 100%"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center"/>
<el-table-column property="language" :label="$t('personVoice.lang')" align="center"/>
<el-table-column property="country" :label="$t('personVoice.country')" align="center"/>
<el-table-column property="name" :label="$t('personVoice.enName')" align="center"/>
<el-table-column property="chineseName" :label="$t('personVoice.chineseName')" align="center"/>
<el-table-column
prop="createTime"
:label="$t('businessMangage.createTime')"
align="center">
<template #default="scope">
{{ getDate(scope.row.createDate) }}
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<div style="margin-top: 20px">
<el-button @click="handleClose"
>取消</el-button
>
<el-button type="primary" @click="addModel()">确认关联</el-button>
</div>
</template>

<script setup>
import { voiceMouldListApi, userVoiceIdListApi, associatedUserVoicesApi } from '@/apis/userManage.js'
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
const { locale } = useI18n();
const lanChange = ref(locale);
const emit = defineEmits(["close"]);
const props = defineProps({
cUserId: {
required: true,
}
});

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD");
};

const multipleTableRef = ref()
const multipleSelection = ref([])
const toggleSelection = (rows) => {
if (rows) {
rows.forEach((row) => {
// TODO: improvement typing when refactor table
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
multipleTableRef.value.toggleRowSelection(row, undefined)
})
} else {
multipleTableRef.value.clearSelection()
}
}
const handleSelectionChange = (val) => {
multipleSelection.value = val
}
const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);
const tableData = ref([]);

const handleSizeChange = (val) => {
pageSize.value = val;
modelAllListFunc(pageNum.value, pageSize.value);
};

const handleCurrentChange = (val) => {
pageNum.value = val;
modelAllListFunc(pageNum.value, pageSize.value);
};
const modelAllListFunc = async (PageNum, PageSize) => {
try {
const res = await voiceMouldListApi(PageNum, 10000);
const modelIds = await userVoiceIdListApi(props.cUserId);
let idIndx = []
if(modelIds.data && modelIds.data.length > 0) {
res.data.list.forEach((ele, idx) => {
modelIds.data.forEach((item) => {
if (ele.id === item) {
idIndx.push(idx)
}
})
})
}
tableData.value = res.data.list;
total.value = res.data.total * 1;
// 选中关联模版
if(idIndx.length) {
setTimeout(()=> {
let list = []
idIndx.forEach(d => {
list.push(tableData.value[d])
})
toggleSelection(list)
}, 500)
}
} catch (error) {
console.log(error, "err");
}
};

const addModel = async () => {
console.log(multipleSelection.value)
let mouldIds = []
multipleSelection.value.forEach(res => {
mouldIds.push(res.id)
})
const data = {
voiceLanguageIdList: mouldIds,
userId: props.cUserId
}
const res = await associatedUserVoicesApi(data);
if(res.code === 200) {
ElMessage.success((lanChange.value) == "zh-cn" ? "关联成功!" : 'Successfully associated!');
handleClose()
}
};
function handleClose() {
emit("close");
}
onMounted(() => {
modelAllListFunc(1, 10);
});
</script>

+ 207
- 0
src/views/userManage/rechargeList.vue ファイルの表示

@@ -0,0 +1,207 @@
<template>
<div class="page justify-around">
<div class="addbtn">
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item :label="$t('rechargeList.phone')">
<el-input v-model="formInline.phone" :placeholder="$t('rechargeList.phonePlaceholder')" clearable />
</el-form-item>
<el-form-item :label="$t('rechargeList.productName')">
<el-input v-model="formInline.productTitle" :placeholder="$t('rechargeList.productNamePlaceholder')" clearable />
</el-form-item>
<el-form-item :label="$t('rechargeList.time')">
<el-date-picker
v-model="formInline.time"
type="datetimerange"
start-placeholder="Start Date"
end-placeholder="End Date"
value-format="YYYY-MM-DD HH:mm:ss"
:default-time="defaultTime"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getListData()">{{ $t('rechargeList.search') }}</el-button>
</el-form-item>
</el-form>
</div>
<el-table
:data="tableData"
style="width: 100%; height: 90%"
size="large"
align="center"
border
>
<el-table-column
prop="orderNumber"
:label="$t('rechargeList.orderNumber')"
align="center"
/>
<el-table-column
prop="productTitle"
:label="$t('rechargeList.productName')"
align="center"
/>
<el-table-column
prop="payVendor"
:label="$t('rechargeList.payVendor')"
align="center"
>
<template #default="scope">
{{ getPayVendor(scope.row.payVendor) }}
</template>
</el-table-column>
<el-table-column
prop="orderPriceStr"
:label="$t('rechargeList.orderPriceStr')"
align="center"
/>
<el-table-column
prop="phone"
:label="$t('rechargeList.phone')"
align="center"
>
<template #default="scope">
{{ scope.row.cuserInfo ? scope.row.cuserInfo.phone : '' }}
</template>
</el-table-column>
<el-table-column
prop="createDate"
:label="$t('rechargeList.createDate')"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createDate) }}
</template>
</el-table-column>
</el-table>
<!-- 登录账号 -->
<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>
<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { rechargeListApi } from '@/apis/userManage.js'
import { useI18n } from "vue-i18n";
import dayjs from "dayjs";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);
const tableData = ref([]);
const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);

const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
] // '12:00:00', '08:00:00'
const getPayVendor = (val) => {
return val === 2 ? "微信" : "支付宝";
};

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD HH:mm:ss");
};
const handleSizeChange = (val) => {
pageSize.value = val;
getListData(pageNum.value, pageSize.value);
};
const handleCurrentChange = (val) => {
pageNum.value = val;
getListData(pageNum.value, pageSize.value);
};
const formInline = ref({
phone: route.query.phone || '',
productTitle: '',
time: ''
})
const getListData = async (PageNum, PageSize) => {
try {
let data = {
PageNum: PageNum || 1,
PageSize: PageSize || 10,
phone: formInline.value.phone,
projectType: 2,
productTitle: formInline.value.productTitle,
startDate: formInline.value.time ? formInline.value.time[0] : '',
endDate: formInline.value.time ? formInline.value.time[1] : '',
}
const res = await rechargeListApi(data);
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};
onMounted(() => {
getListData(1, 10);
});
</script>
<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}
.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 264
- 0
src/views/userManage/userList.vue ファイルの表示

@@ -0,0 +1,264 @@
<template>
<div class="page justify-around">
<div class="addbtn">
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item :label="$t('userList.phone')">
<el-input v-model="formInline.phone" :placeholder="$t('userList.phonePlaceholder')" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getListData()">{{ $t('userList.search') }}</el-button>
</el-form-item>
</el-form>
</div>
<el-table
:data="tableData"
style="width: 100%; height: 90%"
size="large"
align="center"
border
>
<el-table-column
prop="id"
:label="$t('userList.id')"
align="center"
/>
<el-table-column
prop="phone"
:label="$t('userList.phone')"
align="center"
/>


<el-table-column
prop="poins"
:label="$t('userList.poins')"
align="center"
/>
<el-table-column
prop="createDate"
:label="$t('userList.createDate')"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createDate) }}
</template>
</el-table-column>
<el-table-column
fixed="right"
:label="$t('keyMangage.operations')"
align="center"
>
<template #default="scope">
<el-button link type="primary" size="small" @click="setStatus(scope.row)">{{ scope.row.status === 0 ? '封禁' : '恢复' }}</el-button>
<el-button link type="primary" size="small" @click="showDetail(scope.row.id)">{{$t('userList.view')}}</el-button>
<el-button link type="primary" size="small" @click="rechargeDetail(scope.row)">{{$t('userList.rechargeDetail')}}</el-button>
<el-button link type="primary" size="small" @click="videoDetail(scope.row)"> {{$t('userList.videoDetail')}}</el-button>
<el-button link type="primary" size="small" @click="personMould(scope.row.id)">{{$t('userList.personMould')}}</el-button>
<el-button link type="primary" size="small" @click="personVoice(scope.row.id)">{{$t('userList.personVoice')}}</el-button>
</template>
</el-table-column>
</el-table>
<!-- 登录账号 -->
<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<el-dialog v-model="detailDialogVisible" v-if="detailDialogVisible" :title="$t('userList.viewDetail')" width="60%" :before-close="handleClose" >
<el-form :model="form" label-width="120px">
<el-form-item :label="$t('userList.phone')">
<el-input v-model="form.phone" />
</el-form-item>
<el-form-item label="Level">
<el-input v-model="form.level" />
</el-form-item>
<el-form-item :label="$t('userList.poins')">
<el-input v-model="form.poins" />
</el-form-item>
<el-form-item :label="$t('userList.createDate')">
<el-input v-model="form.createDate" />
</el-form-item>
<!-- <el-form-item>
<el-button>{{$t('userList.close')}}</el-button>
</el-form-item> -->
</el-form>
</el-dialog>

<el-dialog v-model="modelDialogVisible" v-if="modelDialogVisible" :title="$t('userList.personMould')" width="60%" :before-close="handleClose" >
<model :cUserId="cUserId" @close="handleClose"></model>
</el-dialog>
<el-dialog v-model="voiceDialogVisible" v-if="voiceDialogVisible" :title="$t('userList.personVoice')" width="60%" :before-close="handleClose" >
<voice :cUserId="cUserId" @close="handleClose"></voice>
</el-dialog>
</div>
</template>
<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入

import { userListApi, userListFindByIdApi, updateStatusApi } from '@/apis/userManage.js'
import { useI18n } from "vue-i18n";
import dayjs from "dayjs";
import { useRoute, useRouter } from "vue-router";
import model from './personMould.vue'
import voice from './personVoice.vue'
const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);
const tableData = ref([]);
const dialogVisible = ref(false);
const rechargeDetail = (item) => {
router.push({
path: "/rechargeList",
query: {
phone: item.phone
},
});
};
const videoDetail = (item) => {
router.push({
path: "/videoList",
query: {
phone: item.phone
},
});
};
const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);
const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD HH:mm:ss");
};
const handleSizeChange = (val) => {
pageSize.value = val;
getListData(pageNum.value, pageSize.value);
};
const handleCurrentChange = (val) => {
pageNum.value = val;
getListData(pageNum.value, pageSize.value);
};

const formInline = ref({
phone: ''
})
const getListData = async (PageNum, PageSize) => {
try {
let data = {
PageNum: PageNum || 1,
PageSize: PageSize || 10,
phone: formInline.value.phone
}
const res = await userListApi(data);
console.log(res, "res");
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};

const form = ref({
name: '',
phone: ''
})
const detailDialogVisible = ref(false)
function showDetail(id) {
userListFindByIdApi(id).then(res => {
detailDialogVisible.value = true
res.data.createDate = getDate(res.data.createDate)
form.value = res.data
})
}
function handleClose() {
detailDialogVisible.value = false
modelDialogVisible.value = false
voiceDialogVisible.value = false
}
const modelDialogVisible = ref(false)
const cUserId = ref('')
function personMould(id) {
modelDialogVisible.value = true
cUserId.value = id
}

const voiceDialogVisible = ref(false)
function personVoice(id) {
voiceDialogVisible.value = true
cUserId.value = id
}

// 状态
function setStatus(item) {
updateStatusApi({id: item.id, status: item.status === 0 ? 1 : 0}).then(res => {
if (res.code === 200) {
ElMessage.success((lanChange.value) == "zh-cn" ? "操作成功!" : 'Operation successful!');
getListData(1, 10);
}
})
}
onMounted(() => {
getListData(1, 10);
});
</script>
<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}
.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 180
- 0
src/views/userManage/userMouldList.vue ファイルの表示

@@ -0,0 +1,180 @@
<template>
<div class="page justify-around">
<div class="addbtn">
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item :label="$t('userMouldList.phone')">
<el-input v-model="formInline.phone" :placeholder="$t('userMouldList.phonePlaceholder')" clearable />
</el-form-item>
<el-form-item :label="$t('userMouldList.mouldSmId')">
<el-input v-model="formInline.mouldSmIdLike" :placeholder="$t('userMouldList.mouldSmIdPlaceholder')" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getListData()">{{$t('userMouldList.search')}}</el-button>
</el-form-item>
</el-form>
</div>
<el-table
:data="tableData"
style="width: 100%; height: 90%"
size="large"
align="center"
border
>
<el-table-column
prop="coverImg"
:label="$t('userMouldList.coverImg')"
align="center"
>
<template #default="scope">
<img :src="scope.row.coverImg" width="50" height="50" alt="" />
</template>
</el-table-column>
<el-table-column
prop="mouldSmId"
:label="$t('userMouldList.mouldSmId')"
align="center"
/>
<el-table-column
prop="wxCUserBasicInfoList"
:label="$t('userMouldList.bindPhone')"
align="center"
>
<template #default="scope">
{{ getListPhone(scope.row.wxCUserBasicInfoList) }}
</template>
</el-table-column>
<el-table-column
prop="createDate"
:label="$t('userMouldList.createDate')"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createDate) }}
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>
<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入

import { userMouldListApi } from '@/apis/userManage.js'
import { useI18n } from "vue-i18n";
import dayjs from "dayjs";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);
const tableData = ref([]);
const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);
const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD");
};
const getListPhone = (list) => {
let phone = []
list && list.forEach(ele => {
phone.push(ele.phone)
});
return phone.join(',')
}

const handleSizeChange = (val) => {
pageSize.value = val;
getListData(pageNum.value, pageSize.value);
};
const handleCurrentChange = (val) => {
pageNum.value = val;
getListData(pageNum.value, pageSize.value);
};

const formInline = ref({
phone: '',
mouldSmIdLike: ''
})
const getListData = async (PageNum, PageSize) => {
try {
let data = {
PageNum: PageNum || 1,
PageSize: PageSize || 10,
phone: formInline.value.phone,
mouldSmIdLike: formInline.value.mouldSmIdLike
}
const res = await userMouldListApi(data);
console.log(res, "res");
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};

onMounted(() => {
getListData(1, 10);
});
</script>
<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}
.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 188
- 0
src/views/userManage/userVoiceList.vue ファイルの表示

@@ -0,0 +1,188 @@
<template>
<div class="page justify-around">
<div class="addbtn">
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item :label="$t('userVoiceList.phone')">
<el-input v-model="formInline.phone" :placeholder="$t('userVoiceList.phonePlaceholder')" clearable />
</el-form-item>
<el-form-item :label="$t('userVoiceList.mouldSmId')">
<el-input v-model="formInline.mouldSmIdLike" :placeholder="$t('userVoiceList.mouldSmIdPlaceholder')" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getListData()"> {{ $t('userVoiceList.search') }}</el-button>
</el-form-item>
</el-form>
</div>

<el-table
:data="tableData"
style="width: 100%; height: 90%"
size="large"
align="center"
border
>
<el-table-column
prop="name"
:label="$t('userVoiceList.enName')"
align="center"
/>

<el-table-column
prop="chineseName"
:label="$t('userVoiceList.chineseName')"
align="center"
/>
<el-table-column
prop="country"
:label="$t('userVoiceList.country')"
align="center"
/>
<el-table-column
prop="language"
:label="$t('userVoiceList.lang')"
align="center"
/>

<el-table-column
prop="wxCUserBasicInfoList"
:label="$t('userVoiceList.bindPhone')"
align="center"
>
<template #default="scope">
{{ getListPhone(scope.row.wxCUserBasicInfoList) }}
</template>
</el-table-column>

<el-table-column
prop="createDate"
:label="$t('userVoiceList.createDate')"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createDate) }}
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入

import { userVoiceListAPi } from '@/apis/userManage.js'

import { useI18n } from "vue-i18n";

import dayjs from "dayjs";

import { useRoute, useRouter } from "vue-router";

const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);

const tableData = ref([]);
const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD");
};

const getListPhone = (list) => {
let phone = []
list && list.forEach(ele => {
phone.push(ele.phone)
});
return phone.join(',')
}


const handleSizeChange = (val) => {
pageSize.value = val;
getListData(pageNum.value, pageSize.value);
};

const handleCurrentChange = (val) => {
pageNum.value = val;
getListData(pageNum.value, pageSize.value);
};

const formInline = ref({
phone: '',
mouldSmIdLike: ''
})

const getListData = async (PageNum, PageSize) => {
try {
let data = {
PageNum: PageNum || 1,
PageSize: PageSize || 10,
phone: formInline.value.phone,
mouldSmIdLike: formInline.value.mouldSmIdLike
}
const res = await userVoiceListAPi(data);
console.log(res, "res");
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};


onMounted(() => {
getListData(1, 10);
});
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}

.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 342
- 0
src/views/userManage/videoList.vue ファイルの表示

@@ -0,0 +1,342 @@
<template>
<div class="page justify-around">
<div class="addbtn">
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item :label="$t('videoList.phone')">
<el-input v-model="formInline.phone" :placeholder="$t('videoList.phonePlaceholder')" clearable />
</el-form-item>
<el-form-item :label="$t('videoList.text')">
<el-input v-model="formInline.paperwork" :placeholder="$t('videoList.textPlaceholder')" clearable />
</el-form-item>
<el-form-item :label="$t('videoList.type')">
<el-select
v-model="formInline.videoType"
:placeholder="$t('videoList.typePlaceholder')"
clearable
>
<el-option :label="$t('videoList.all')" value="" />
<el-option :label="$t('videoList.portrait')" value="1" />
<el-option :label="$t('videoList.transversePlate')" value="2" />
</el-select>
</el-form-item>
<el-form-item :label="$t('videoList.status')">
<el-select
v-model="formInline.isDel"
:placeholder="$t('videoList.statusPlaceholder')"
clearable
>
<el-option :label="$t('videoList.all')" value="" />
<el-option :label="$t('videoList.notDeleted')" value="0" />
<el-option :label="$t('videoList.deleted')" value="1" />
</el-select>
</el-form-item>
<el-form-item :label="$t('videoList.time')">
<el-date-picker
v-model="formInline.time"
type="datetimerange"
start-placeholder="Start Date"
end-placeholder="End Date"
value-format="YYYY-MM-DD HH:mm:ss"
:default-time="defaultTime"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getListData()">{{ $t('videoList.search') }}</el-button>
</el-form-item>
</el-form>
</div>
<el-table
:data="tableData"
style="width: 100%; height: 90%"
size="large"
align="center"
border
>
<el-table-column
prop="videoType"
:label="$t('videoList.type')"
align="center"
>
<template #default="scope">
{{ getVideoType(scope.row.videoType) }}
</template>
</el-table-column>
<el-table-column
prop="paperwork"
:label="$t('videoList.text')"
align="center"
>
<template #default="scope">
{{ scope.row.paperwork.substring(0, 10) + '...' }}
</template>
</el-table-column>
<el-table-column
prop="isDel"
:label="$t('videoList.status')"
align="center"
>
<template #default="scope">
{{ getIsDel(scope.row.isDel) }}
</template>
</el-table-column>
<el-table-column
prop="title"
:label="$t('videoList.title')"
align="center"
/>
<el-table-column
prop="coverImg"
:label="$t('videoList.coverImg')"
align="center"
>
<template #default="scope">
<img :src="scope.row.coverImg" width="50" height="50" alt="" />
</template>
</el-table-column>
<el-table-column
prop="videoPlayUrl"
:label="$t('videoList.videoPlayUrl')"
align="center"
/>
<el-table-column
prop="videoTime"
:label="$t('videoList.videoTime')"
align="center"
>
</el-table-column>
<el-table-column
prop="videoSize"
:label="$t('videoList.videoSize')"
align="center"
/>
<el-table-column
prop="videoMsg"
:label="$t('videoList.videoMsg')"
align="center"
/>
<el-table-column
prop="costPoints"
:label="$t('videoList.costPoints')"
align="center"
/>
<el-table-column
prop="costPointsDetail"
:label="$t('videoList.costPointsDetail')"
align="center"
/>
<el-table-column
prop="createDate"
:label="$t('videoList.createDate')"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createDate) }}
</template>
</el-table-column>

<el-table-column
fixed="right"
:label="$t('keyMangage.operations')"
align="center"
>
<template #default="scope">
<el-button link type="primary" size="small" @click="showDetail(scope.row.id)">{{$t('videoList.view')}}</el-button>
<el-button v-if="scope.row.isDel === 0" link type="primary" size="small" @click="confirmDel(scope.row.id)">{{$t('videoList.del')}}</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<el-dialog v-model="dialogVisible" v-if="dialogVisible" :title="`${$t('videoList.videoDetail')}:`" width="40%">
<el-form :model="form" label-width="120px">
<el-form-item :label="$t('videoList.name')">
<el-input v-model="form.title" />
</el-form-item>
<el-form-item :label="$t('videoList.videType')">
<el-radio-group v-model="form.videoType">
<el-radio :label="1">{{$t('videoList.portrait')}}</el-radio>
<el-radio :label="2">{{$t('videoList.transversePlate')}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('videoList.text')">
<el-input v-model="form.paperwork" type="textarea" />
</el-form-item>
<el-form-item :label="$t('videoList.coverImg')">
<img :src="form.coverImg" alt="" :style="{ width: form.videoType === 1 ? '50%' : '100%'}">
</el-form-item>
<el-form-item :label="$t('videoList.videoPlayUrl')">
<el-input v-model="form.videoPlayUrl" />
<video :src="form.videoPlayUrl" controls :style="{ width: form.videoType === 1 ? '50%' : '100%'}"></video>
</el-form-item>
<el-form-item :label="$t('videoList.videoTime')">
<el-input v-model="form.videoTime" />
</el-form-item>
<el-form-item :label="$t('videoList.createDate')">
<el-input v-model="form.createDate" />
</el-form-item>
<el-form-item :label="$t('videoList.costPoints')">
<el-input v-model="form.costPoints" />
</el-form-item>
<el-form-item :label="$t('videoList.costPointsDetail')">
<el-input v-model="form.costPointsDetail" />
</el-form-item>
<el-form-item>
<el-button @click="dialogVisible = false">{{ $t('videoList.close') }}</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script setup>
//#region 导入
import { videoListApi, videoListFindByIdApi, videoListDelApi } from '@/apis/userManage.js'
import { useI18n } from "vue-i18n";
import dayjs from "dayjs";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
const lanChange = ref(locale);
const tableData = ref([]);
const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);

const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
] // '12:00:00', '08:00:00'
const getVideoType = (val) => {
return val === 1 ? "竖版" : "横版";
};
const getIsDel = (val) => {
return val === 1 ? "已删除" : "未删除";
};

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD HH:mm:ss");
};
const handleSizeChange = (val) => {
pageSize.value = val;
getListData(pageNum.value, pageSize.value);
};
const handleCurrentChange = (val) => {
pageNum.value = val;
getListData(pageNum.value, pageSize.value);
};
const formInline = ref({
phone: route.query.phone || '',
productTitle: '',
time: ''
})
const getListData = async (PageNum, PageSize) => {
try {
let data = {
PageNum: PageNum || 1,
PageSize: PageSize || 10,
phone: formInline.value.phone,
projectType: 2,
productTitle: formInline.value.productTitle,
startDate: formInline.value.time ? formInline.value.time[0] : '',
endDate: formInline.value.time ? formInline.value.time[1] : '',
id: route.query.id || '',
isDel: formInline.value.isDel,
videoType: formInline.value.videoType,
paperwork: formInline.value.paperwork
}
const res = await videoListApi(data);
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};
// 查看详情
const dialogVisible = ref(false)
const form = ref({})
function showDetail(id) {
videoListFindByIdApi(id).then(res => {
if(res.code === 200){
dialogVisible.value = true
form.value = res.data
}
})
}
function confirmDel(id) {
ElMessageBox.confirm('你确定要删除吗?').then(() => {
videoListDelApi(id).then(res => {
if (res.code == 200) {
ElMessage.success("删除成功!");
getListData(1, 10)
}
})
done()
}).catch(() => {
// catch error
})
}
onMounted(() => {
getListData(1, 10);
});
</script>
<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}
.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 87
- 0
src/views/userManage/voiceMould.vue ファイルの表示

@@ -0,0 +1,87 @@
<template>
<div>
<el-checkbox-group v-model="checkboxGroup" size="small" class="checkbox">
<el-checkbox v-for="(item, index) in tableData" :key="index" :label="item.id" border>
<img :src="item.coverImg" width="50" />
<p>{{ item.title }}</p>
</el-checkbox>
</el-checkbox-group>
<div style="margin-top: 20px">
<el-button @click="handleClose"
>{{ $t('personMould.cancel') }}</el-button
>
<el-button type="primary" @click="addModel()">{{ $t('personMould.confirm') }}</el-button>
</div>
</div>
</template>

<script setup>
import { ref } from 'vue'
import { voiceMouldListApi, userVoiceIdListApi, associatedUserVoicesApi } from '@/apis/userManage.js'
import dayjs from "dayjs";

const emit = defineEmits(["close"]);
const props = defineProps({
serviceId: {
required: true,
}
});

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD");
};

const checkboxGroup = ref([])
const total = ref(0);
const tableData = ref([]);

const modelAllListFunc = async () => {
try {
const res = await voiceMouldListApi(1, 10000);
const modelIds = await userVoiceIdListApi(props.serviceId);
tableData.value = res.data.list;
total.value = res.data.total * 1;
setTimeout(()=> {
if(modelIds.data && modelIds.data.length > 0) {
modelIds.data.forEach((item) => {
checkboxGroup.value.push(item)
})
}
}, 500)

} catch (error) {
console.log(error, "err");
}
};

const addModel = async () => {
const data = {
mouldIds: checkboxGroup.value,
cUserId: props.serviceId
}
const res = await associatedUserVoicesApi(data);
if(res.code === 200) {
ElMessage.success("关联成功!");
handleClose()
}
};

function handleClose() {
emit("close");
}
onMounted(() => {
modelAllListFunc(1, 10);
});
</script>

<style lang="scss" scoped>
.checkbox {
max-height: 500px;
overflow: auto;
:deep .el-checkbox.el-checkbox--small {
width: 110px;
height:110px;
margin-bottom: 10px;
}
}
</style>

+ 175
- 0
src/views/video/index.vue ファイルの表示

@@ -0,0 +1,175 @@
<template>
<div class="page justify-around">
<el-alert :title="`${$t('video.time')}:${crrentVideoToal} 秒`" type="success" :closable="false" />
<el-table
:data="tableData"
style="width: 100%; height: 90%"
size="large"
align="center"
border
>
<el-table-column
prop="serviceId"
:label="$t('video.code')"
align="center">
<template #default="scope">
<el-button type="primary" text @click="getDetail(scope.row.serviceId)">{{ scope.row.serviceId }}</el-button>
</template>
</el-table-column>
<el-table-column
prop="videoTime"
:label="$t('video.videoTime')"
align="center"
/>
<el-table-column
prop="videoUrl"
:label="$t('video.videoUrl')"
align="center"
/>
<el-table-column
prop="createTime"
:label="$t('video.createTime')"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createTime) }}
</template>
</el-table-column>

<el-table-column
fixed="right"
:label="$t('keyMangage.operations')"
align="center"
>
<template #default="scope">
<el-button link type="primary" size="small" @click="showDetail(scope.row.userMouldVideoId)">{{ $t('keyMangage.detail') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>
<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { videoListApi,currentVideoToalApi } from '@/apis/home.js'
import { useI18n } from "vue-i18n";
import dayjs from "dayjs";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
const tableData = ref([]);
const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);
const getDate = (val) => {
return dayjs(Number(val)).format("YYYY-MM-DD HH:mm:ss");
};

// 跳转视频列表
const showDetail = (id) => {
router.push({
path: "/videoList",
query: {
id,
},
});
};
const handleSizeChange = (val) => {
pageSize.value = val;
getBusinessListFunc(pageNum.value, pageSize.value);
};
const handleCurrentChange = (val) => {
pageNum.value = val;
getBusinessListFunc(pageNum.value, pageSize.value);
};
const getBusinessListFunc = async (PageNum, PageSize) => {
try {
const res = await videoListApi(PageNum, PageSize);
console.log(res, "res");
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};

//查看详情
function getDetail(id) {
router.push({
path: "/apiManage",
query: {
id,
},
});
}
const crrentVideoToal = ref('')
function getCrrentVideoToal() {
currentVideoToalApi().then(res => {
crrentVideoToal.value = res.data
})
}
onMounted(() => {
getCrrentVideoToal()
getBusinessListFunc(1, 10);
});
</script>
<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}
.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 16
- 11
vite.config.ts ファイルの表示

@@ -35,26 +35,31 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
`,
},
},
postcss: {
plugins: [
postCssPxToRem({
rootValue: 192, // 1rem 的大小
propList: ['*'], // 需要转换的属性,*(全部转换)
unitPrecision: 6 // 转换精度,保留的小数位数
})
]
}
// postcss: {
// plugins: [
// postCssPxToRem({
// rootValue: 192, // 1rem 的大小
// propList: ['*'], // 需要转换的属性,*(全部转换)
// unitPrecision: 6 // 转换精度,保留的小数位数
// })
// ]
// }
},
server: {
host: "0.0.0.0",
port: Number(env.VITE_APP_PORT),
open: true, // 运行是否自动打开浏览器
// "/api": {
// target: env.VITE_APP_BASE_API, // 线上接口地址
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, ""),
// },
proxy: {
// 反向代理解决跨域
"/api": {
"/A": {
target: env.VITE_APP_BASE_API, // 线上接口地址
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
rewrite: (path) => path.replace(/^\/A/, ""),
},
},
},


+ 123
- 0
vite.config.ts.timestamp-1698900171336-585e6054472a3.mjs
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 235
- 164
yarn-error.log
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 1619
- 1624
yarn.lock
ファイル差分が大きすぎるため省略します
ファイルの表示


読み込み中…
キャンセル
保存