Commit 8aeb6fa1 by haojie

Initial commit

parents
VITE_BASE_API='http://snow.test'
VITE_BASE_API=''
\ No newline at end of file
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Type Support For `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
//引入路径模块
import path from 'path';
//引入文件模块
import fs from 'fs';
import { parse } from 'node-html-parser';
import { fileURLToPath } from 'url';
const __filenameNew = fileURLToPath(import.meta.url);
const __dirnameNew = path.dirname(__filenameNew);
// 因打包文件名是获取的当前时间,所以每次修改index.html记得在这修改文件名
let pathName = path.join(
__dirnameNew,
'Snow-mobile-2023-1-12--18.33/index.html'
);
fs.readFile(pathName, 'utf8', function (err, html) {
if (err) {
return console.log('读取index.html文件失败' + err.message);
}
const root = parse(html);
const elList = root.querySelectorAll('script');
for (let i = 0; i < elList.length; i++) {
// 1、移除 <script type=module> 元素
const data = elList[i].getAttribute('type');
const cro = elList[i].hasAttribute('crossorigin');
if (data && data === 'module' && cro) {
elList[i].remove();
}
// 2、移除其他 <script> 的 nomodule 属性
const hasNoModule = elList[i].hasAttribute('nomodule');
if (hasNoModule) {
elList[i].removeAttribute('nomodule');
}
// 3、移除 <script id=vite-legacy-entry> 元素的内容,并把 data-src 属性名改为 src
// const hasDataSrc = elList[i].hasAttribute('data-src');
// if (hasDataSrc) {
// const legacyEle = elList[i];
// const srcData = legacyEle.getAttribute('data-src');
// legacyEle.setAttribute('src', srcData);
// legacyEle.removeAttribute('data-src');
// legacyEle.innerText = '';
// }
}
const ellink = root.querySelectorAll('link');
for (let j = 0; j < ellink.length; j++) {
// 4.移除 link标签上的modulepreload
let rel = ellink[j].getAttribute('rel');
if (rel && rel == 'modulepreload') {
ellink[j].removeAttribute('rel');
}
}
// 将新的组合的内容写入原有index.html
fs.writeFileSync(pathName, root.toString());
// console.log('222'+root.toString())
});
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
/>
<title>GPT</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "snow-mobile",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --open --mode development",
"dev:app": "vite --mode app",
"build": "vue-tsc && vite build",
"build:app": "vue-tsc && vite build --mode app",
"build:app2": "vite build --mode app",
"preview": "vite preview"
},
"dependencies": {
"@vitejs/plugin-legacy": "^3.0.1",
"@vitejs/plugin-vue-jsx": "^3.0.0",
"dayjs": "^1.11.7",
"js-cookie": "^3.0.1",
"tdesign-icons-vue-next": "^0.1.7",
"tdesign-vue-next": "^1.0.5",
"uuid": "^9.0.0",
"vue": "^3.2.45",
"vue-clipboard3": "^2.0.0",
"vue-router": "^4.1.6",
"vuex": "^4.1.0"
},
"devDependencies": {
"@types/js-cookie": "^3.0.2",
"@types/node": "^18.11.15",
"@types/uuid": "^9.0.0",
"@vitejs/plugin-vue": "^4.0.0",
"axios": "^0.24.0",
"express": "^4.18.2",
"http-proxy-middleware": "^2.0.6",
"less": "^4.1.1",
"serve": "^14.2.0",
"terser": "^5.16.1",
"typescript": "^4.9.3",
"vite": "^4.0.0",
"vite-plugin-compression": "^0.5.1",
"vite-svg-loader": "^3.1.0",
"vue-tsc": "^1.0.11"
}
}
192.168.1.1:3000
\ No newline at end of file
<svg width="23" height="26" viewBox="0 0 23 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.5919 0.5C11.9687 0.5 12.2741 0.800513 12.2741 1.17121V5.57183L14.6679 3.2164C14.9342 2.95427 15.3662 2.95427 15.6325 3.2164C15.8989 3.47853 15.8989 3.90352 15.6325 4.16564L12.2741 7.47031V11.8362L16.1225 9.64986L17.3502 5.14146C17.4477 4.78339 17.8218 4.5709 18.1857 4.66684C18.5496 4.76279 18.7655 5.13084 18.668 5.48891L17.7934 8.70062L21.6627 6.50246C21.989 6.31711 22.4062 6.4271 22.5946 6.74814C22.7829 7.06918 22.6711 7.47969 22.3449 7.66504L18.4718 9.86535L21.7417 10.7275C22.1056 10.8234 22.3216 11.1915 22.2241 11.5496C22.1266 11.9076 21.7525 12.1201 21.3886 12.0242L16.8009 10.8146L12.9584 12.9975L16.8069 15.1838L21.3886 13.9758C21.7525 13.8799 22.1266 14.0924 22.2241 14.4504C22.3216 14.8085 22.1056 15.1766 21.7417 15.2725L18.4778 16.1331L22.3471 18.3312C22.6733 18.5166 22.7851 18.9271 22.5967 19.2481C22.4084 19.5692 21.9912 19.6792 21.6649 19.4938L17.7918 17.2935L18.668 20.5111C18.7655 20.8692 18.5496 21.2372 18.1857 21.3332C17.8218 21.4291 17.4477 21.2166 17.3502 20.8585L16.1209 16.3443L12.2741 14.1589V18.5407L15.6325 21.8454C15.8989 22.1075 15.8989 22.5325 15.6325 22.7946C15.3662 23.0567 14.9342 23.0567 14.6679 22.7946L12.2741 20.4392V24.8288C12.2741 25.1995 11.9687 25.5 11.5919 25.5C11.2152 25.5 10.9098 25.1995 10.9098 24.8288V20.4435L8.52039 22.7946C8.254 23.0567 7.82209 23.0567 7.5557 22.7946C7.28931 22.5325 7.28931 22.1075 7.5557 21.8454L10.9098 18.545V14.1613L7.05762 16.3498L5.82833 20.864C5.73083 21.2221 5.35678 21.4346 4.99288 21.3387C4.62899 21.2427 4.41303 20.8747 4.51054 20.5166L5.38672 17.299L1.52332 19.4938C1.19706 19.6792 0.779871 19.5692 0.591504 19.2481C0.403136 18.9271 0.514921 18.5166 0.841184 18.3312L4.7008 16.1386L1.43681 15.278C1.07291 15.1821 0.856957 14.814 0.954464 14.4559C1.05197 14.0979 1.42601 13.8854 1.78991 13.9813L6.3717 15.1893L10.2298 12.9975L6.37765 10.8091L1.78991 12.0187C1.42601 12.1146 1.05197 11.9021 0.95446 11.5441C0.856953 11.186 1.07291 10.8179 1.43681 10.722L4.70676 9.85984L0.843363 7.66504C0.517099 7.47969 0.405314 7.06918 0.593682 6.74814C0.78205 6.4271 1.19924 6.31711 1.5255 6.50246L5.38512 8.69512L4.51054 5.4834C4.41303 5.12533 4.62898 4.75728 4.99288 4.66134C5.35678 4.56539 5.73083 4.77789 5.82833 5.13596L7.05602 9.64436L10.9098 11.8337V7.46602L7.5557 4.16564C7.28931 3.90352 7.28931 3.47853 7.5557 3.2164C7.82209 2.95427 8.254 2.95427 8.52039 3.2164L10.9098 5.56754V1.17121C10.9098 0.800513 11.2152 0.5 11.5919 0.5Z" fill="#2962FF"/>
</svg>
\ No newline at end of file
const path = require('path');
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
// const history = require('connect-history-api-fallback');
const app = express();
// 处理单页应用路由
// app.use(history());
// 代理对象地址
// 读取本地配置的ip
app.use(
'/video',
createProxyMiddleware({
target: 'http://192.168.1.19:5000',
changeOrigin: true,
// pathRewrite: {
// '^api': '',
// },
})
);
// 加载静态资源
app.use(express.static('./dist'));
// 启动服务
app.listen(3001, () => {
console.log('success => http://localhost:3001');
});
<template>
<router-view v-slot="{ Component }">
<component :is="Component" />
</router-view>
</template>
<script setup lang="ts"></script>
<style lang="less">
#app {
background: var(--theme-color-38);
& > :nth-child(1) {
background: var(--theme-color-1);
}
}
</style>
<svg width="23" height="26" viewBox="0 0 23 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.5919 0.5C11.9687 0.5 12.2741 0.800513 12.2741 1.17121V5.57183L14.6679 3.2164C14.9342 2.95427 15.3662 2.95427 15.6325 3.2164C15.8989 3.47853 15.8989 3.90352 15.6325 4.16564L12.2741 7.47031V11.8362L16.1225 9.64986L17.3502 5.14146C17.4477 4.78339 17.8218 4.5709 18.1857 4.66684C18.5496 4.76279 18.7655 5.13084 18.668 5.48891L17.7934 8.70062L21.6627 6.50246C21.989 6.31711 22.4062 6.4271 22.5946 6.74814C22.7829 7.06918 22.6711 7.47969 22.3449 7.66504L18.4718 9.86535L21.7417 10.7275C22.1056 10.8234 22.3216 11.1915 22.2241 11.5496C22.1266 11.9076 21.7525 12.1201 21.3886 12.0242L16.8009 10.8146L12.9584 12.9975L16.8069 15.1838L21.3886 13.9758C21.7525 13.8799 22.1266 14.0924 22.2241 14.4504C22.3216 14.8085 22.1056 15.1766 21.7417 15.2725L18.4778 16.1331L22.3471 18.3312C22.6733 18.5166 22.7851 18.9271 22.5967 19.2481C22.4084 19.5692 21.9912 19.6792 21.6649 19.4938L17.7918 17.2935L18.668 20.5111C18.7655 20.8692 18.5496 21.2372 18.1857 21.3332C17.8218 21.4291 17.4477 21.2166 17.3502 20.8585L16.1209 16.3443L12.2741 14.1589V18.5407L15.6325 21.8454C15.8989 22.1075 15.8989 22.5325 15.6325 22.7946C15.3662 23.0567 14.9342 23.0567 14.6679 22.7946L12.2741 20.4392V24.8288C12.2741 25.1995 11.9687 25.5 11.5919 25.5C11.2152 25.5 10.9098 25.1995 10.9098 24.8288V20.4435L8.52039 22.7946C8.254 23.0567 7.82209 23.0567 7.5557 22.7946C7.28931 22.5325 7.28931 22.1075 7.5557 21.8454L10.9098 18.545V14.1613L7.05762 16.3498L5.82833 20.864C5.73083 21.2221 5.35678 21.4346 4.99288 21.3387C4.62899 21.2427 4.41303 20.8747 4.51054 20.5166L5.38672 17.299L1.52332 19.4938C1.19706 19.6792 0.779871 19.5692 0.591504 19.2481C0.403136 18.9271 0.514921 18.5166 0.841184 18.3312L4.7008 16.1386L1.43681 15.278C1.07291 15.1821 0.856957 14.814 0.954464 14.4559C1.05197 14.0979 1.42601 13.8854 1.78991 13.9813L6.3717 15.1893L10.2298 12.9975L6.37765 10.8091L1.78991 12.0187C1.42601 12.1146 1.05197 11.9021 0.95446 11.5441C0.856953 11.186 1.07291 10.8179 1.43681 10.722L4.70676 9.85984L0.843363 7.66504C0.517099 7.47969 0.405314 7.06918 0.593682 6.74814C0.78205 6.4271 1.19924 6.31711 1.5255 6.50246L5.38512 8.69512L4.51054 5.4834C4.41303 5.12533 4.62898 4.75728 4.99288 4.66134C5.35678 4.56539 5.73083 4.77789 5.82833 5.13596L7.05602 9.64436L10.9098 11.8337V7.46602L7.5557 4.16564C7.28931 3.90352 7.28931 3.47853 7.5557 3.2164C7.82209 2.95427 8.254 2.95427 8.52039 3.2164L10.9098 5.56754V1.17121C10.9098 0.800513 11.2152 0.5 11.5919 0.5Z" fill="#2962FF"/>
</svg>
\ No newline at end of file
<svg width="110" height="30" viewBox="0 0 110 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M49.921 20.4016C49.2216 20.9476 48.1828 21.2206 46.8047 21.2206C45.5499 21.2206 44.4906 21.0295 43.6267 20.6473C42.7628 20.2469 42.0943 19.8465 41.6212 19.4461C41.3332 19.5371 41.0761 19.6918 40.8498 19.9102C40.6441 20.1104 40.5413 20.3652 40.5413 20.6746C40.5413 21.0386 40.7984 21.4117 41.3126 21.7939C41.8474 22.1579 42.5776 22.4672 43.5033 22.722C44.4495 22.9586 45.5499 23.0769 46.8047 23.0769C48.9439 23.0769 50.5998 22.631 51.7722 21.7393C52.9653 20.8475 53.5618 19.6827 53.5618 18.245C53.5618 16.8982 53.0887 15.8426 52.1425 15.0783C51.2169 14.3139 49.9415 13.7315 48.3165 13.3311L46.4344 12.8397C45.3031 12.5485 44.4803 12.2027 43.9661 11.8024C43.4724 11.402 43.2256 10.8833 43.2256 10.2463C43.2256 9.44552 43.5753 8.81764 44.2746 8.36266C44.974 7.88947 45.9202 7.65288 47.1132 7.65288C48.1417 7.65288 49.0879 7.78937 49.9518 8.06236C50.8363 8.33536 51.5357 8.65385 52.0499 9.01784C52.2762 8.90864 52.4613 8.76304 52.6053 8.58105C52.7493 8.38085 52.8213 8.16246 52.8213 7.92587C52.8213 7.56188 52.5642 7.21609 52.0499 6.8885C51.5562 6.56091 50.8672 6.29702 49.9827 6.09682C49.1188 5.87843 48.1314 5.76923 47.0207 5.76923C45.1077 5.76923 43.5753 6.16962 42.4234 6.9704C41.2715 7.75297 40.6955 8.84494 40.6955 10.2463C40.6955 10.9925 40.8601 11.6477 41.1892 12.2118C41.5389 12.7578 42.0326 13.2219 42.6702 13.6041C43.3079 13.9681 44.0587 14.2684 44.9226 14.505L47.6069 15.2148C48.7794 15.5059 49.633 15.8972 50.1678 16.3886C50.7232 16.88 51.0009 17.4988 51.0009 18.245C51.0009 19.1185 50.6409 19.8374 49.921 20.4016Z" fill="#4375FF"/>
<path d="M67.9556 10.9015C69.0457 11.6477 69.5908 12.8124 69.5908 14.3958V22.6674C69.488 22.7038 69.3234 22.7402 69.0972 22.7766C68.8915 22.8312 68.6755 22.8585 68.4492 22.8585C67.9967 22.8585 67.6573 22.7857 67.431 22.6401C67.2253 22.4763 67.1225 22.2215 67.1225 21.8758V14.505C67.1225 13.4858 66.7934 12.7396 66.1351 12.2664C65.4975 11.7933 64.6336 11.5567 63.5434 11.5567C62.7206 11.5567 61.9904 11.6477 61.3527 11.8297C60.7151 12.0116 60.17 12.2209 59.7174 12.4575V22.6674C59.6146 22.7038 59.4603 22.7402 59.2546 22.7766C59.0489 22.8312 58.8227 22.8585 58.5758 22.8585C58.1439 22.8585 57.8148 22.7857 57.5885 22.6401C57.3622 22.4763 57.2491 22.2215 57.2491 21.8758V12.5667C57.2491 12.1845 57.3314 11.8752 57.4959 11.6386C57.6811 11.3838 57.9896 11.1381 58.4216 10.9015C58.9564 10.6285 59.666 10.3737 60.5505 10.1371C61.435 9.88231 62.4326 9.75491 63.5434 9.75491C65.4152 9.75491 66.8859 10.1371 67.9556 10.9015Z" fill="#4375FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M87.4494 16.4159C87.4494 17.7627 87.1615 18.9365 86.5855 19.9375C86.0096 20.9385 85.1971 21.712 84.148 22.2579C83.099 22.8039 81.8648 23.0769 80.4455 23.0769C79.0262 23.0769 77.7818 22.8039 76.7121 22.2579C75.6631 21.712 74.8506 20.9385 74.2747 19.9375C73.6987 18.9365 73.4107 17.7627 73.4107 16.4159C73.4107 15.051 73.6987 13.8771 74.2747 12.8943C74.8712 11.8934 75.694 11.1199 76.743 10.5739C77.8126 10.0279 79.0468 9.75491 80.4455 9.75491C81.8442 9.75491 83.0681 10.0279 84.1172 10.5739C85.1662 11.1199 85.9787 11.8934 86.5547 12.8943C87.1512 13.8771 87.4494 15.051 87.4494 16.4159ZM80.4455 11.5567C79.0674 11.5567 77.9669 11.9934 77.1441 12.867C76.3419 13.7224 75.9408 14.9054 75.9408 16.4159C75.9408 17.9447 76.3316 19.1367 77.1133 19.9921C77.9155 20.8475 79.0262 21.2752 80.4455 21.2752C81.8442 21.2752 82.9344 20.8475 83.7161 19.9921C84.5183 19.1185 84.9194 17.9265 84.9194 16.4159C84.9194 14.9054 84.5183 13.7224 83.7161 12.867C82.9344 11.9934 81.8442 11.5567 80.4455 11.5567Z" fill="#4375FF"/>
<path d="M100.065 13.3311C99.6947 14.4777 99.2936 15.6242 98.8617 16.7708C98.4503 17.9174 98.0492 18.9911 97.6584 19.9921C97.2675 20.9931 96.9076 21.8576 96.5785 22.5855C96.4139 22.6765 96.2288 22.7493 96.0231 22.8039C95.8174 22.8585 95.5397 22.8858 95.19 22.8858C94.8609 22.8858 94.5524 22.8312 94.2644 22.722C93.997 22.631 93.8119 22.4763 93.709 22.2579C93.5033 21.894 93.2668 21.3662 92.9994 20.6746C92.7525 19.983 92.4851 19.1913 92.1971 18.2996C91.9297 17.4078 91.6521 16.4887 91.3641 15.5423C91.0761 14.5778 90.8087 13.6496 90.5619 12.7578C90.3356 11.8661 90.1402 11.0744 89.9756 10.3828C90.1196 10.2554 90.2945 10.1371 90.5002 10.0279C90.7264 9.91871 90.9938 9.86411 91.3024 9.86411C91.6932 9.86411 91.9812 9.96421 92.1663 10.1644C92.372 10.3464 92.516 10.6467 92.5983 11.0653C92.7834 11.8297 92.9891 12.6941 93.2153 13.6587C93.4622 14.6233 93.709 15.5787 93.9558 16.5251C94.2233 17.4715 94.4598 18.3269 94.6655 19.0912C94.8918 19.8374 95.0563 20.3925 95.1592 20.7565H95.2826C95.4471 20.3197 95.7043 19.6099 96.0539 18.6272C96.4036 17.6262 96.815 16.4887 97.2881 15.2148C97.7818 13.9408 98.2755 12.6486 98.7691 11.3383C98.9337 11.2473 99.1291 11.1836 99.3553 11.1472C99.5816 11.0926 99.8284 11.0653 100.096 11.0653C100.466 11.0653 100.775 11.129 101.021 11.2564C101.268 11.3656 101.433 11.5385 101.515 11.7751C102.009 13.0308 102.472 14.2684 102.904 15.4877C103.336 16.6889 103.716 17.7627 104.045 18.7091C104.374 19.6372 104.611 20.3288 104.755 20.7838H104.909C105.382 19.164 105.896 17.426 106.452 15.5696C107.007 13.7133 107.46 11.8934 107.809 10.1098C108.097 9.94601 108.447 9.86411 108.858 9.86411C109.208 9.86411 109.486 9.93691 109.692 10.0825C109.897 10.2281 110 10.4556 110 10.765C110 10.9652 109.928 11.3474 109.784 11.9116C109.661 12.4757 109.486 13.14 109.26 13.9044C109.033 14.6688 108.776 15.4877 108.488 16.3613C108.221 17.2167 107.943 18.0539 107.655 18.8728C107.367 19.6736 107.089 20.4016 106.822 21.0568C106.575 21.712 106.359 22.2215 106.174 22.5855C106.071 22.6583 105.896 22.722 105.65 22.7766C105.423 22.8494 105.156 22.8858 104.847 22.8858C104.004 22.8858 103.51 22.7129 103.366 22.3671C103.078 21.712 102.749 20.9021 102.379 19.9375C102.009 18.9547 101.628 17.8992 101.237 16.7708L100.065 13.3311Z" fill="#4375FF"/>
<path d="M15.456 0C15.9583 0 16.3655 0.360616 16.3655 0.805458V6.0862L19.5572 3.25968C19.9124 2.94513 20.4883 2.94513 20.8435 3.25968C21.1987 3.57423 21.1987 4.08422 20.8435 4.39877L16.3655 8.36438V13.6034L21.4968 10.9798L23.1337 5.56975C23.2637 5.14007 23.7624 4.88508 24.2476 5.00021C24.7328 5.11534 25.0208 5.55701 24.8908 5.98669L23.7247 9.84075L28.8837 7.20295C29.3187 6.98053 29.875 7.11253 30.1262 7.49777C30.3773 7.88302 30.2283 8.37563 29.7932 8.59805L24.6291 11.2384L28.9891 12.273C29.4743 12.3881 29.7622 12.8298 29.6322 13.2595C29.5022 13.6892 29.0035 13.9442 28.5183 13.829L22.4013 12.3775L17.2779 14.997L22.4092 17.6206L28.5183 16.171C29.0035 16.0558 29.5022 16.3108 29.6322 16.7405C29.7622 17.1702 29.4743 17.6119 28.9891 17.727L24.6371 18.7597L29.7962 21.3975C30.2312 21.6199 30.3802 22.1125 30.1291 22.4978C29.8779 22.883 29.3216 23.015 28.8866 22.7926L23.7225 20.1522L24.8908 24.0133C25.0208 24.443 24.7328 24.8847 24.2476 24.9998C23.7624 25.1149 23.2637 24.8599 23.1337 24.4302L21.4947 19.0131L16.3655 16.3906V21.6488L20.8435 25.6144C21.1987 25.929 21.1987 26.439 20.8435 26.7535C20.4883 27.0681 19.9124 27.0681 19.5572 26.7535L16.3655 23.927V29.1945C16.3655 29.6394 15.9583 30 15.456 30C14.9537 30 14.5465 29.6394 14.5465 29.1945V23.9322L11.3606 26.7535C11.0054 27.0681 10.4295 27.0681 10.0743 26.7535C9.71916 26.439 9.71916 25.929 10.0743 25.6144L14.5465 21.654V16.3936L9.41024 19.0197L7.77119 24.4368C7.64118 24.8665 7.14246 25.1215 6.65726 25.0064C6.17206 24.8913 5.88412 24.4496 6.01413 24.0199L7.18237 20.1588L2.03118 22.7926C1.59616 23.015 1.03991 22.883 0.788753 22.4978C0.537596 22.1125 0.686642 21.6199 1.12166 21.3975L6.26782 18.7663L1.91583 17.7336C1.43063 17.6185 1.14269 17.1768 1.2727 16.7471C1.40271 16.3174 1.90143 16.0625 2.38663 16.1776L8.49568 17.6272L13.6399 14.997L8.50362 12.3709L2.38662 13.8224C1.90143 13.9375 1.4027 13.6826 1.27269 13.2529C1.14269 12.8232 1.43062 12.3815 1.91582 12.2664L6.27576 11.2318L1.12457 8.59805C0.689547 8.37562 0.540501 7.88301 0.791658 7.49777C1.04281 7.11252 1.59907 6.98053 2.03409 7.20295L7.18024 9.83414L6.01413 5.98009C5.88412 5.5504 6.17206 5.10874 6.65726 4.99361C7.14246 4.87847 7.64118 5.13347 7.77119 5.56315L9.4081 10.9732L14.5465 13.6004V8.35923L10.0743 4.39877C9.71916 4.08422 9.71916 3.57423 10.0743 3.25968C10.4295 2.94513 11.0054 2.94513 11.3606 3.25968L14.5465 6.08105V0.805458C14.5465 0.360616 14.9537 0 15.456 0Z" fill="#4375FF"/>
</svg>
\ No newline at end of file
<svg width="35" height="35" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M29.6406 14.1458C27.1498 14.1458 24.7275 13.3292 22.7354 11.8169V22.3606C22.7354 27.7448 18.6258 32.1052 13.5552 32.1052C8.48458 32.1052 4.375 27.7448 4.375 22.3606C4.375 16.9765 8.48458 12.6146 13.5552 12.6146C14.0627 12.6146 14.5542 12.6583 15.034 12.7429V18.3283C14.5755 18.1457 14.0866 18.0517 13.5931 18.0512C11.3313 18.0512 9.49521 19.9981 9.49521 22.4015C9.49521 24.8004 11.3313 26.7487 13.5931 26.7487C15.8535 26.7487 17.6881 24.8004 17.6881 22.4015V1.45833H22.8083C22.8083 5.48333 25.884 8.74562 29.6771 8.74562V14.1415L29.6406 14.1444" fill="#03FBFF"/>
<path d="M30.59 15.5808C28.0954 15.58 25.6697 14.7625 23.6834 13.2533V23.7971C23.6834 29.1812 19.5738 33.5417 14.5032 33.5417C9.43399 33.5417 5.3244 29.1812 5.3244 23.7971C5.3244 18.4129 9.43399 14.0525 14.5032 14.0525C15.0107 14.0525 15.5036 14.0962 15.9834 14.1808V19.7663C15.5249 19.5837 15.036 19.4896 14.5425 19.4892C12.2807 19.4892 10.4446 21.436 10.4446 23.8394C10.4446 26.2398 12.2807 28.1881 14.5425 28.1881C16.8029 28.1881 18.6375 26.2398 18.6375 23.8394V2.89479H23.7577C23.7577 6.91979 26.8334 10.1821 30.6265 10.1821V15.5779L30.59 15.5808Z" fill="#FD1753"/>
</svg>
\ No newline at end of file
<svg width="45" height="45" viewBox="0 0 45 45" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="22.5" cy="22.5" r="22.5" fill="#393939"/>
<path d="M34.6406 19.1458C32.1498 19.1458 29.7275 18.3292 27.7354 16.8169V27.3606C27.7354 32.7448 23.6258 37.1052 18.5552 37.1052C13.4846 37.1052 9.375 32.7448 9.375 27.3606C9.375 21.9765 13.4846 17.6146 18.5552 17.6146C19.0627 17.6146 19.5542 17.6583 20.034 17.7429V23.3283C19.5755 23.1457 19.0866 23.0517 18.5931 23.0512C16.3313 23.0512 14.4952 24.9981 14.4952 27.4015C14.4952 29.8004 16.3313 31.7487 18.5931 31.7487C20.8535 31.7487 22.6881 29.8004 22.6881 27.4015V6.45833H27.8083C27.8083 10.4833 30.884 13.7456 34.6771 13.7456V19.1415L34.6406 19.1444" fill="#03FBFF"/>
<path d="M35.5901 20.5808C33.0955 20.58 30.6697 19.7625 28.6834 18.2533V28.7971C28.6834 34.1812 24.5738 38.5417 19.5032 38.5417C14.434 38.5417 10.3245 34.1812 10.3245 28.7971C10.3245 23.4129 14.434 19.0525 19.5032 19.0525C20.0107 19.0525 20.5036 19.0962 20.9834 19.1808V24.7663C20.525 24.5837 20.0361 24.4896 19.5426 24.4892C17.2807 24.4892 15.4447 26.436 15.4447 28.8394C15.4447 31.2398 17.2807 33.1881 19.5426 33.1881C21.803 33.1881 23.6376 31.2398 23.6376 28.8394V7.89479H28.7578C28.7578 11.9198 31.8334 15.1821 35.6265 15.1821V20.5779L35.5901 20.5808Z" fill="#FD1753"/>
</svg>
\ No newline at end of file
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.1 9.375C21.4167 9.375 21.7208 9.4375 22.0125 9.5625C22.3042 9.6875 22.5542 9.85417 22.7625 10.0625C22.9708 10.2708 23.1375 10.5208 23.2625 10.8125C23.3875 11.1042 23.45 11.4083 23.45 11.725C23.45 12.0583 23.3875 12.3667 23.2625 12.65C23.1375 12.9333 22.9708 13.1792 22.7625 13.3875C22.5542 13.5958 22.3042 13.7625 22.0125 13.8875C21.7208 14.0125 21.4167 14.075 21.1 14.075H14.075V21.1C14.075 21.4333 14.0125 21.7417 13.8875 22.025C13.7625 22.3083 13.5958 22.5542 13.3875 22.7625C13.1792 22.9708 12.9292 23.1375 12.6375 23.2625C12.3458 23.3875 12.0417 23.45 11.725 23.45C11.3917 23.45 11.0833 23.3875 10.8 23.2625C10.5167 23.1375 10.2708 22.9708 10.0625 22.7625C9.85417 22.5542 9.6875 22.3083 9.5625 22.025C9.4375 21.7417 9.375 21.4333 9.375 21.1V14.075H2.35C2.01667 14.075 1.70833 14.0125 1.425 13.8875C1.14167 13.7625 0.895833 13.5958 0.6875 13.3875C0.479167 13.1792 0.3125 12.9333 0.1875 12.65C0.0625001 12.3667 0 12.0583 0 11.725C0 11.4083 0.0625001 11.1042 0.1875 10.8125C0.3125 10.5208 0.479167 10.2708 0.6875 10.0625C0.895833 9.85417 1.14167 9.6875 1.425 9.5625C1.70833 9.4375 2.01667 9.375 2.35 9.375H9.375V2.35C9.375 2.03333 9.4375 1.72917 9.5625 1.4375C9.6875 1.14583 9.85417 0.895833 10.0625 0.6875C10.2708 0.479167 10.5167 0.3125 10.8 0.1875C11.0833 0.0625 11.3917 0 11.725 0C12.375 0 12.9292 0.229167 13.3875 0.6875C13.8458 1.14583 14.075 1.7 14.075 2.35V9.375H21.1Z" fill="#FD1753"/>
<path d="M21.1 9.375C21.4167 9.375 21.7208 9.4375 22.0125 9.5625C22.3042 9.6875 22.5542 9.85417 22.7625 10.0625C22.9708 10.2708 23.1375 10.5208 23.2625 10.8125C23.3875 11.1042 23.45 11.4083 23.45 11.725C23.45 12.0583 23.3875 12.3667 23.2625 12.65C23.1375 12.9333 22.9708 13.1792 22.7625 13.3875C22.5542 13.5958 22.3042 13.7625 22.0125 13.8875C21.7208 14.0125 21.4167 14.075 21.1 14.075H14.075V21.1C14.075 21.4333 14.0125 21.7417 13.8875 22.025C13.7625 22.3083 13.5958 22.5542 13.3875 22.7625C13.1792 22.9708 12.9292 23.1375 12.6375 23.2625C12.3458 23.3875 12.0417 23.45 11.725 23.45C11.3917 23.45 11.0833 23.3875 10.8 23.2625C10.5167 23.1375 10.2708 22.9708 10.0625 22.7625C9.85417 22.5542 9.6875 22.3083 9.5625 22.025C9.4375 21.7417 9.375 21.4333 9.375 21.1V14.075H2.35C2.01667 14.075 1.70833 14.0125 1.425 13.8875C1.14167 13.7625 0.895833 13.5958 0.6875 13.3875C0.479167 13.1792 0.3125 12.9333 0.1875 12.65C0.0625001 12.3667 0 12.0583 0 11.725C0 11.4083 0.0625001 11.1042 0.1875 10.8125C0.3125 10.5208 0.479167 10.2708 0.6875 10.0625C0.895833 9.85417 1.14167 9.6875 1.425 9.5625C1.70833 9.4375 2.01667 9.375 2.35 9.375H9.375V2.35C9.375 2.03333 9.4375 1.72917 9.5625 1.4375C9.6875 1.14583 9.85417 0.895833 10.0625 0.6875C10.2708 0.479167 10.5167 0.3125 10.8 0.1875C11.0833 0.0625 11.3917 0 11.725 0C12.375 0 12.9292 0.229167 13.3875 0.6875C13.8458 1.14583 14.075 1.7 14.075 2.35V9.375H21.1Z" fill="#FD1753"/>
</svg>
\ No newline at end of file
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.24265 3.88809C1.04704 3.69958 0.897759 3.48131 0.794807 3.23327C0.691855 2.98523 0.640378 2.73719 0.640379 2.48916C0.640379 2.24112 0.691855 1.99308 0.794808 1.74505C0.897761 1.49701 1.04704 1.27873 1.24265 1.09023C1.44855 0.891796 1.67763 0.745456 1.92986 0.651202C2.18209 0.556947 2.4369 0.509821 2.69428 0.50982C2.95166 0.50982 3.20904 0.559427 3.46642 0.658643C3.7238 0.757857 3.9503 0.901719 4.14591 1.09023L8.48535 5.27213L12.8248 1.09023C13.0307 0.891798 13.2598 0.745455 13.512 0.651202C13.7642 0.556947 14.019 0.50982 14.2764 0.50982C14.5338 0.509821 14.7912 0.559427 15.0486 0.658643C15.3059 0.757858 15.5324 0.901718 15.7281 1.09023C15.934 1.28866 16.0858 1.50941 16.1836 1.75249C16.2814 1.99556 16.3303 2.24112 16.3303 2.48916C16.3303 2.73719 16.2814 2.98275 16.1836 3.22583C16.0858 3.4689 15.934 3.68966 15.7281 3.88809L11.3886 8.06999L15.7281 12.2519C15.934 12.4503 16.0858 12.6711 16.1836 12.9142C16.2814 13.1572 16.3303 13.4028 16.3303 13.6508C16.3303 13.8989 16.2814 14.1444 16.1836 14.3875C16.0858 14.6306 15.934 14.8513 15.7281 15.0498C15.5324 15.2383 15.3059 15.3821 15.0486 15.4813C14.7912 15.5806 14.5338 15.6302 14.2764 15.6302C14.019 15.6302 13.7642 15.583 13.512 15.4888C13.2598 15.3945 13.0307 15.2482 12.8248 15.0498L8.48535 10.8679L4.14591 15.0498C3.9503 15.2383 3.7238 15.3821 3.46642 15.4813C3.20904 15.5806 2.95166 15.6302 2.69428 15.6302C2.4369 15.6302 2.18209 15.583 1.92986 15.4888C1.67762 15.3945 1.44856 15.2482 1.24265 15.0498C0.841136 14.6628 0.640379 14.1965 0.640379 13.6508C0.640379 13.1051 0.841136 12.6388 1.24265 12.2519L5.58209 8.06999L1.24265 3.88809Z" fill="#FD1753"/>
<path d="M1.24265 3.88809C1.04704 3.69958 0.897759 3.48131 0.794807 3.23327C0.691855 2.98523 0.640378 2.73719 0.640379 2.48916C0.640379 2.24112 0.691855 1.99308 0.794808 1.74505C0.897761 1.49701 1.04704 1.27873 1.24265 1.09023C1.44855 0.891796 1.67763 0.745456 1.92986 0.651202C2.18209 0.556947 2.4369 0.509821 2.69428 0.50982C2.95166 0.50982 3.20904 0.559427 3.46642 0.658643C3.7238 0.757857 3.9503 0.901719 4.14591 1.09023L8.48535 5.27213L12.8248 1.09023C13.0307 0.891798 13.2598 0.745455 13.512 0.651202C13.7642 0.556947 14.019 0.50982 14.2764 0.50982C14.5338 0.509821 14.7912 0.559427 15.0486 0.658643C15.3059 0.757858 15.5324 0.901718 15.7281 1.09023C15.934 1.28866 16.0858 1.50941 16.1836 1.75249C16.2814 1.99556 16.3303 2.24112 16.3303 2.48916C16.3303 2.73719 16.2814 2.98275 16.1836 3.22583C16.0858 3.4689 15.934 3.68966 15.7281 3.88809L11.3886 8.06999L15.7281 12.2519C15.934 12.4503 16.0858 12.6711 16.1836 12.9142C16.2814 13.1572 16.3303 13.4028 16.3303 13.6508C16.3303 13.8989 16.2814 14.1444 16.1836 14.3875C16.0858 14.6306 15.934 14.8513 15.7281 15.0498C15.5324 15.2383 15.3059 15.3821 15.0486 15.4813C14.7912 15.5806 14.5338 15.6302 14.2764 15.6302C14.019 15.6302 13.7642 15.583 13.512 15.4888C13.2598 15.3945 13.0307 15.2482 12.8248 15.0498L8.48535 10.8679L4.14591 15.0498C3.9503 15.2383 3.7238 15.3821 3.46642 15.4813C3.20904 15.5806 2.95166 15.6302 2.69428 15.6302C2.4369 15.6302 2.18209 15.583 1.92986 15.4888C1.67762 15.3945 1.44856 15.2482 1.24265 15.0498C0.841136 14.6628 0.640379 14.1965 0.640379 13.6508C0.640379 13.1051 0.841136 12.6388 1.24265 12.2519L5.58209 8.06999L1.24265 3.88809Z" fill="#616161"/>
</svg>
\ No newline at end of file
<svg width="40" height="30" viewBox="0 0 40 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22.125 20.8333V25.4167H30.4583C33.375 25 35.6667 22.2917 35.6667 19.1667C35.6667 15.625 32.9583 12.9167 29.4167 12.9167C28.5833 12.9167 27.9583 13.125 27.3333 13.3333V12.9167C27.3333 8.33334 23.5833 4.58334 19 4.58334C14.4167 4.58334 10.6667 8.33334 10.6667 12.9167C10.6667 13.75 10.875 14.375 10.875 15.2083C10.4583 15 10.0417 15 9.625 15C6.70833 15 4.41667 17.2917 4.41667 20.2083C4.41667 23.125 6.70833 25.4167 9.625 25.4167H17.9583V20.8333L15.6667 23.125L12.75 20.2083L20.0417 12.9167L27.3333 20.2083L24.4167 23.125L22.125 20.8333ZM22.125 25.4167V29.5833H17.9583V25.4167H15.875V29.5833H9.625C4.41667 29.5833 0.25 25.4167 0.25 20.2083C0.25 16.0417 2.95833 12.5 6.5 11.25C7.33333 5.20834 12.5417 0.416672 19 0.416672C24.4167 0.416672 29.2083 3.95834 30.875 8.75C35.875 9.375 39.8333 13.75 39.8333 19.1667C39.8333 24.5833 35.6667 28.9583 30.4583 29.5833H24.2083V25.4167H22.125Z" fill="#999999"/>
<path d="M22.125 20.8333V25.4167H30.4583C33.375 25 35.6667 22.2917 35.6667 19.1667C35.6667 15.625 32.9583 12.9167 29.4167 12.9167C28.5833 12.9167 27.9583 13.125 27.3333 13.3333V12.9167C27.3333 8.33334 23.5833 4.58334 19 4.58334C14.4167 4.58334 10.6667 8.33334 10.6667 12.9167C10.6667 13.75 10.875 14.375 10.875 15.2083C10.4583 15 10.0417 15 9.625 15C6.70833 15 4.41667 17.2917 4.41667 20.2083C4.41667 23.125 6.70833 25.4167 9.625 25.4167H17.9583V20.8333L15.6667 23.125L12.75 20.2083L20.0417 12.9167L27.3333 20.2083L24.4167 23.125L22.125 20.8333ZM22.125 25.4167V29.5833H17.9583V25.4167H15.875V29.5833H9.625C4.41667 29.5833 0.25 25.4167 0.25 20.2083C0.25 16.0417 2.95833 12.5 6.5 11.25C7.33333 5.20834 12.5417 0.416672 19 0.416672C24.4167 0.416672 29.2083 3.95834 30.875 8.75C35.875 9.375 39.8333 13.75 39.8333 19.1667C39.8333 24.5833 35.6667 28.9583 30.4583 29.5833H24.2083V25.4167H22.125Z" fill="#999999"/>
</svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
\ No newline at end of file
<template>
<t-popup v-model="dialog_visible" placement="bottom" :lock-scroll="false">
<div class="custom-change-language-popup">
<template v-for="item in options" :key="item.value">
<div class="btn" @click="onChange(item.value)">{{ item.label }}</div>
</template>
</div>
</t-popup>
</template>
<script lang="ts" setup>
import { ref } from "@vue/reactivity";
import { watch } from "@vue/runtime-core";
const props = defineProps({
visible: Boolean,
options: Object as any,
});
const emit = defineEmits(["update:visible", "change"]);
const dialog_visible = ref(false);
watch(
() => props.visible,
(v) => {
dialog_visible.value = v;
}
);
watch(
() => dialog_visible.value,
(v) => {
emit("update:visible", v);
}
);
const onChange = (value: string) => {
emit("update:visible", false);
emit("change", value);
};
</script>
<style lang="less" scoped>
@import url(~@/style/flex.less);
.custom-change-language-popup {
width: 100vw;
background: var(--theme-color2);
.btn {
border-bottom: 1px solid var(--theme-color38);
font-size: 15px;
color: var(--theme-color9);
height: 60px;
.dja();
}
.btn:last-child {
border-bottom: none;
}
}
</style>
<template>
<div
class="custom-loading-box"
:style="{
width: width,
height: height,
background: background,
position: position,
}"
:class="{ 'custom-is-table-box': !isTable }"
>
<div
class="ball-beat"
:style="{ top: top, left: left }"
:class="{ 'custom-is-table-child': !isTable }"
>
<div></div>
<div></div>
<div></div>
</div>
</div>
</template>
<script lang="ts" setup>
const props = withDefaults(
defineProps<{
background?: string;
position?: string | any;
width?: string;
left?: string;
height?: string;
top?: string;
isTable?: boolean;
}>(),
{
background: '',
position: 'absolute',
width: '100%',
left: '0px',
height: '100%',
top: '0px',
isTable: false,
}
);
</script>
<style lang="less" scoped>
@import '@/style/flex.less';
.custom-loading-box {
left: 0;
top: 0;
// -webkit-transform: translateY(-50%) translateX(-50%);
// transform: translateY(-50%) translateX(-50%);
z-index: 100;
box-sizing: border-box;
.ball-beat {
position: sticky;
.dja();
}
.ball-beat > div {
background-color: #4999ff;
width: 14px;
height: 14px;
border-radius: 100% !important;
margin: 2px;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
display: inline-block;
-webkit-animation: ball-beat 0.7s 0s infinite linear;
animation: ball-beat 0.7s 0s infinite linear;
z-index: 300;
}
.ball-beat > div:nth-child(2n-1) {
-webkit-animation-delay: 0.35s !important;
animation-delay: 0.35s !important;
}
}
.custom-is-table-box {
.dja();
.custom-is-table-child {
.dj();
position: relative;
}
}
@-webkit-keyframes ball-beat {
50% {
opacity: 0.2;
-webkit-transform: scale(0.75);
transform: scale(0.75);
}
100% {
opacity: 1;
-webkit-transform: scale(1);
transform: scale(1);
}
}
@keyframes ball-beat {
50% {
opacity: 0.2;
-webkit-transform: scale(0.75);
transform: scale(0.75);
}
100% {
opacity: 1;
-webkit-transform: scale(1);
transform: scale(1);
}
}
</style>
<template>
<div class="custom-navbar">
<slot name="left">
<span @click="back"> <BackSvg></BackSvg></span>
</slot>
<div class="custom-navbar-content">
<slot name="content"> <div></div> </slot>
</div>
<slot name="right"> <div class="default-right"></div> </slot>
</div>
</template>
<script lang="ts" setup>
import BackSvg from '@/assets/svg/login/back.svg?component';
const emit = defineEmits(['routerSubmit']);
const back = () => {
emit('routerSubmit');
};
</script>
<style lang="less">
@import '@/style/flex.less';
.custom-navbar {
.dja(space-between);
height: 6%;
.default-right {
width: 20px;
}
.custom-navbar-content {
color: var(--theme-color-4);
}
}
</style>
.custom-t-button {
height: 36px;
}
import { defineComponent } from 'vue';
import { Button as TButton } from 'tdesign-vue-next';
import './index.less';
export default defineComponent({
setup(props, { slots, emit }) {
return () => (
<TButton class="custom-t-button">
{slots.default ? slots.default() : ''}
</TButton>
);
},
});
export interface RulesType {
required?: boolean;
type?: string;
message?: string;
min?: number;
max?: number;
validator?: Function;
}
<template>
<div class="custom-input-global">
<div
class="custom-input-box"
:class="{ 'custom-input-error': ruleError.status }"
>
<slot name="leftIcon">
<span class="left-input-icon"></span>
</slot>
<input
:type="input_type"
v-model="input_value"
class="cust-input"
:disabled="disabled"
:placeholder="placeholder"
@focus="onInputFocus"
@blur="onInputBlur"
@input="numberInput(input_value)"
/>
<slot name="rightIcon">
<span v-if="type === 'password'" class="custom-pwd-hide-button">
<PrivatePwdSvg
v-if="Cur_pwd_type === 'private'"
@click="changePwd('text')"
></PrivatePwdSvg>
<PublicPwdSvg v-else @click="changePwd('password')"></PublicPwdSvg>
</span>
</slot>
<!-- remember -->
<transition name="remember-fade">
<div
class="remember-select-box"
v-if="needSelect && input_focus && selectList.length"
>
<div
v-for="item in selectList"
:key="item.account"
class="line"
@click="QuickInputAccount(item)"
>
<div class="account">{{ item.account }}</div>
<div class="password">********</div>
</div>
</div>
</transition>
</div>
<div class="custom-input-rule" v-if="rules && rules.length">
{{ ruleError.message }}
</div>
</div>
</template>
<script lang="ts" setup>
import PrivatePwdSvg from '@/assets/svg/login/privatePwd.svg?component';
import PublicPwdSvg from '@/assets/svg/login/publicPwd.svg?component';
import { reactive, ref } from '@vue/reactivity';
import { watch } from '@vue/runtime-core';
import { RulesType } from './Interfase';
import { emailReg } from '@/constants/token';
const input_value = ref<string>('');
const props = withDefaults(
defineProps<{
type?: string;
placeholder?: string;
num?: number;
rules?: Array<RulesType>;
modelValue: string;
disabled?: boolean;
needSelect?: boolean;
selectList?: any;
}>(),
{
// 输入框类型
type: 'text',
placeholder: '',
needSelect: false,
selectList: [],
// rules: [],
disabled: false,
}
);
const emit = defineEmits([
'update:modelValue',
'submitType',
'submitAccount',
'inputChange',
]);
// 是否聚焦
const input_focus = ref(false);
// 校验显示的错误
const ruleError = reactive({
status: false,
message: '',
});
const input_type = ref<string>('text');
// 当输入框类型为password时,展示私有密码svg
const Cur_pwd_type = ref<string>('private');
// 输入框校验
const numberInput = (e: string) => {
const { type } = props;
if (type == 'number') {
input_value.value = e.replace(/[^\d.]/g, '');
}
// 提交输入事件
emit('inputChange', input_value.value);
};
// 聚焦事件
const onInputFocus = () => {
if (props.needSelect) {
input_focus.value = true;
}
};
// 失去焦点
const onInputBlur = () => {
if (props.needSelect) {
input_focus.value = false;
}
};
// 错误状态
const errorinput = (rule: any) => {
ruleError.status = true;
ruleError.message = rule.message;
// 提交失败信息
emit('submitType', false);
};
// 提交用户的账号密码
const QuickInputAccount = (item: any) => {
emit('submitAccount', item);
};
// 重置输入框状态
const ResetInput = () => {
ruleError.status = false;
ruleError.message = '';
// 提交通过信息
emit('submitType', true);
};
if (props.type == 'password') {
input_type.value = props.type;
}
//
const changePwd = (value: string) => {
if (value === 'text') {
Cur_pwd_type.value = 'public';
} else {
Cur_pwd_type.value = 'private';
}
input_type.value = value;
};
// 表单校验
const FormValidation = () => {
const { rules } = props;
let v = input_value.value;
if (rules) {
for (let i in rules) {
let rule: any = rules[i];
if (rule.required && !v) {
// 校验空input
errorinput(rule);
return;
} else {
ResetInput();
}
// 自定义校验逻辑
if (rule.validator) {
let res = rule.validator(v);
if (!res.status) {
errorinput(res);
return;
} else {
ResetInput();
}
}
// 邮箱验证模块
if (rule.type == 'email') {
if (!emailReg.test(v)) {
// 邮箱校验不通过
errorinput(rule);
return;
} else {
ResetInput();
}
}
// 输入框最小长度校验
if (rule.min && v.length < rule.min) {
errorinput(rule);
return;
} else {
ResetInput();
}
}
}
};
watch(
() => input_value.value,
(v) => {
emit('update:modelValue', v);
// 判断输入的内容是否符合校验规则
// 校验
FormValidation();
}
);
//
watch(
() => props.num,
(v) => {
// 校验
FormValidation();
}
);
watch(
() => props.modelValue,
(v) => {
input_value.value = v;
}
);
</script>
<style lang="less">
@import '@/style/flex.less';
.custom-input-global {
height: 100%;
.custom-input-box {
height: 48px;
background: var(--theme-color-15);
/* 背景线条 */
border: 1px solid var(--theme-color-19);
border-radius: 8px;
transition: all 0.3s;
position: relative;
.da();
.cust-input {
height: 100%;
width: 0;
flex: 1;
outline: none;
border: none;
border-radius: 8px;
padding-left: 12px;
background: transparent;
color: var(--theme-color-4);
}
.left-input-icon {
.da();
}
.custom-pwd-hide-button {
.da();
padding: 0 8px;
}
.remember-select-box {
z-index: 200;
position: absolute;
top: 46px;
width: 100%;
box-shadow: 0 3px 14px 2px rgba(0, 0, 0, 0.05),
0 8px 10px 1px rgba(0, 0, 0, 6%), 0 5px 5px -3px rgba(0, 0, 0, 10%);
background: var(--theme-color-34);
border-radius: 4px;
max-height: 200px;
overflow-y: auto;
.line {
padding: 6px 12px;
font-weight: bold;
.account {
color: var(--theme-color-35);
}
.password {
color: var(--theme-color-4);
}
}
}
}
.custom-input-error {
border-color: #f05451 !important;
transition: all 0.3s;
}
.custom-input-rule {
color: #f05451;
font-weight: 400;
font-size: 12px;
line-height: 26px;
padding-left: 4px;
min-height: 26px;
}
}
//内容打开动画
.remember-fade-enter-active {
transition: all 0.15s ease-out;
}
.remember-fade-leave-active {
transition: all 0.15s cubic-bezier(1, 0.5, 0.8, 1);
}
.remember-fade-enter-from,
.remember-fade-leave-to {
transform: translateY(-6px);
opacity: 0;
}
</style>
.custom-t-loading {
.t-icon-loading {
font-size: 18px;
}
}
import { defineComponent } from 'vue';
import './index.less';
export default defineComponent({
props: {
loading: Boolean,
},
setup(props, { slots }) {
return () => (
<t-loading class="custom-t-loading" v-show={props.loading}></t-loading>
);
},
});
<template>
<div class="custom-buy-sell-setp-title">
<div class="dot">{{ dot }}</div>
<div class="title-text">{{ text }}</div>
</div>
</template>
<script lang="ts" setup>
const props = withDefaults(
defineProps<{
dot?: number;
text?: string;
}>(),
{
dot: 1,
text: '默认文字',
}
);
</script>
<style lang="less">
@import '@/style/flex.less';
.custom-buy-sell-setp-title {
.da();
line-height: 46px;
.dot {
width: 20px;
height: 20px;
border-radius: 50%;
background: #2962ff;
color: #eaecef;
.dja();
font-size: 12px;
}
.title-text {
margin-left: 6px;
font-weight: 600;
font-size: 16px;
color: var(--theme-color-4);
}
}
</style>
<template>
<div class="m-switch-wrap">
<div
@click="disabled ? (e: any) => e.preventDefault() : onSwitch()"
:class="['m-switch', { 'switch-checked': checked, disabled: disabled }]"
>
<div
:class="[
'u-switch-inner',
checked ? 'inner-checked' : 'inner-unchecked',
]"
>
{{ checked ? checkedInfo : uncheckedInfo }}
</div>
<div :class="['u-node', { 'node-checked': checked }]">
<themeLight v-show="!checked" class="icon"></themeLight>
<themeDark v-show="checked" class="icon"></themeDark>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from '@vue/reactivity';
import { computed, watch } from '@vue/runtime-core';
import { useStore } from 'vuex';
import themeLight from '@/assets/svg/user/theme-switch-light.svg?component';
import themeDark from '@/assets/svg/user/theme-switch-dark.svg?component';
const props = defineProps({
defaultChecked: {
// 初始是否选中
type: Boolean,
default: false,
},
checkedInfo: {
// 选中时的内容
type: [Number, String],
default: null,
},
uncheckedInfo: {
// 未选中时的内容
type: [Number, String],
default: null,
},
disabled: {
// 是否禁用
type: Boolean,
default: false,
},
});
const store = useStore();
const theme = computed(() => store.getters['theme/getTheme']);
const checked = ref(false);
watch(
() => theme.value,
(v) => {
if (v) {
let html = document.children[0];
html.setAttribute('theme-mode', v);
if (v === 'dark') {
checked.value = true;
} else {
checked.value = false;
}
} else {
checked.value = false;
}
},
{
immediate: true,
}
);
const onSwitch = () => {
if (!checked.value) {
store.commit('theme/setTheme', 'dark');
} else {
store.commit('theme/setTheme', 'light');
}
checked.value = !checked.value;
};
</script>
<style lang="less" scoped>
@import '@/style/flex.less';
@themeColor: #1890ff;
.m-switch-wrap {
height: 22px;
min-width: 54px;
display: inline-block;
.m-switch {
position: relative;
height: 24px;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
background: transparent;
border-radius: 12px;
border: 1px solid #dddddd;
border-radius: 12px;
transition: background 0.36s;
.u-switch-inner {
display: inline-block;
color: #fff;
font-size: 14px;
line-height: 22px;
padding: 0 8px;
transition: all 0.36s;
}
.inner-checked {
margin-right: 18px;
}
.inner-unchecked {
margin-left: 18px;
}
.u-node {
position: absolute;
top: 2px;
left: 2px;
width: 18px;
height: 18px;
background: #fff;
border-radius: 100%;
transition: all 0.36s;
.dja();
.icon {
width: 80%;
height: 80%;
}
}
.node-checked {
// 结果等价于right: 2px; 为了滑动效果都以左边为基准进行偏移
left: 100%;
margin-left: -2px;
transform: translateX(-100%);
}
}
.switch-checked {
background: transparent;
}
.disabled {
cursor: not-allowed;
opacity: 0.4;
}
}
</style>
export const TOKEN_NAME = 'dexnav-token';
// app版本地cookie
export const APP_COOKIE = 'app-cookie';
// 登录页面三个模块的名称
export const LOGIN_MODES = {
login: 'login',
register: 'register',
forgot: 'forgot',
code: 'code',
forgotMode: {
account: 'account',
code: 'code',
password: 'password',
},
};
export const tabPanels = [
{
value: "first",
label: "登录",
panel: () => <div>1</div>,
},
{
value: "second",
label: "注册",
panel: () => <div>2</div>,
},
];
// 网站名称
export const Web_name = "SILKR";
/**
* 各个页面的手续费接口type参数
*/
//提现
export const config_type_withDrawal = 1;
// 现货
export const config_type_shop_trading = 2;
// 合约
export const config_type_contract_trading = 3;
// 预售
export const config_type_sale_trading = 4;
// 赚币宝
export const config_type_pledge_trading = 5;
// 矿机
export const config_type_mining_trading = 6;
// 邮箱正则
export const emailReg = /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,8}){1,2}$/;
// 邀请码
let dddcode = "79o0";
// 用户余额保留几位小数
export const user_ba_decimal_places = 4;
// sessionStorage-key-现货交易当前展示买或者卖
export const TRADE_TYPE = "trade_type";
// 记住密码存入本地的key--
export const remember_password_phone = "remember_phone";
export const remember_password_email = "remember_email";
// 客服
export const CustomerServiceLink =
"https://api.whatsapp.com/send/?phone=639054988009&text&type=phone_number&app_absent=0";
import { ref, Ref, onUnmounted } from 'vue';
/**
* counter utils--验证码倒计时
* @param duration
* @returns
*/
export const useCounter = (duration = 60): [Ref<number>, any, () => void] => {
let intervalTimer: any;
onUnmounted(() => {
clearInterval(intervalTimer);
});
const countDown = ref(0);
const openInterval = () => {
countDown.value = duration;
intervalTimer = setInterval(() => {
if (countDown.value > 0) {
countDown.value -= 1;
} else {
clearInterval(intervalTimer);
countDown.value = 0;
}
}, 1000);
};
const closeInterval = () => {
if (intervalTimer) {
clearInterval(intervalTimer);
}
};
return [countDown, openInterval, closeInterval];
};
import useClipboard from 'vue-clipboard3';
import { MessagePlugin } from 'tdesign-vue-next';
export default function () {
const doCopy = (keyword: string, toast: boolean = true) => {
if (!keyword) {
return;
}
const { toClipboard } = useClipboard();
toClipboard(keyword)
.then(() => {
if (toast) {
MessagePlugin.success('复制成功');
}
})
.catch((e: any) => {
if (toast) {
MessagePlugin.error('复制失败');
}
});
};
return {
doCopy,
};
}
<template>
<div class="custom-layout">
<Header v-if="route.meta.header"></Header>
<div class="custom-content">
<router-view v-slot="{ Component }">
<component :is="Component" />
</router-view>
</div>
</div>
</template>
<script lang="ts" setup>
import Header from './header.vue';
import { useRoute } from 'vue-router';
const route = useRoute();
</script>
<style lang="less">
@import '@/style/flex.less';
.custom-layout {
height: 100%;
.dj();
flex-direction: column;
height: 100vh;
.custom-content {
flex: 1;
background: #f9f9f9;
max-height: calc(100vh - 60px);
overflow: auto;
padding: 40px 0;
box-sizing: border-box;
& > :first-child {
width: 80%;
min-width: 1000px;
margin: 0 auto;
height: 100%;
background: white;
}
}
}
</style>
<template>
<div class="custom-layout-head">
<div class="layout-head-left">
<LeftSvg></LeftSvg>
<span>AI</span>
<div
class="layout-chose-button"
v-for="item in btns"
:key="item.path"
@click="changeBtn(item)"
:class="{ active: item.path === currentBtn }"
>
{{ item.label }}
</div>
</div>
<div class="layout-head-right">
<t-button v-if="token" class="logout" @click="logout"> 退出 </t-button>
<RightSvg></RightSvg>
</div>
</div>
</template>
<script lang="ts" setup>
import LeftSvg from '@/assets/svg/header/headerLeft.svg?component';
import RightSvg from '@/assets/svg/header/headerRight.svg?component';
import { ref } from '@vue/reactivity';
import { computed } from '@vue/runtime-core';
import { useRoute, useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { useLogout } from '@/utils/api/userApi';
import { MessagePlugin, Button as TButton } from 'tdesign-vue-next';
const store = useStore();
const token = computed(() => store.getters['user/token']);
const route = useRoute();
const router = useRouter();
const currentBtn = ref(route.path);
const btns = [
{
label: '图片生成',
path: '/',
},
];
const logout = async () => {
try {
let res: any = await useLogout();
if (res.code == 0) {
store.commit('user/removeToken');
MessagePlugin.success('退出成功');
router.replace({
path: '/',
});
}
} catch (e) {
console.log(e);
}
};
const changeBtn = (item: any) => {
currentBtn.value = item.path;
router.replace({
path: item.path,
});
};
</script>
<style lang="less">
@import '@/style/flex.less';
.custom-layout-head {
height: 60px;
background: #ffffff;
border-bottom: 1px solid #dbdbdb;
.dja(space-between);
padding: 0 12px;
.layout-head-left {
.da();
span {
font-weight: 700;
font-size: 24px;
}
.layout-chose-button {
margin-left: 80px;
font-size: 18px;
color: #413f3f;
transition: all 0.1s;
cursor: pointer;
}
.active {
color: #fd1753;
font-size: 20px;
font-weight: 600;
transition: all 0.1s;
}
}
.layout-head-right {
.da();
.logout {
background: #fd1753;
border: none;
margin-right: 20px;
--ripple-color: #fd6053 !important;
}
}
}
</style>
import { createApp } from 'vue';
import '@/style/index.less';
import router from './router';
import store from './store';
import App from './App.vue';
// 引入组件库全局样式资源
import 'tdesign-vue-next/es/style/index.css';
import '@/style/ui.less';
// 全局守卫
import './router/auth';
const app = createApp(App);
app.use(router);
app.use(store);
app.mount('#app');
<template>
<t-dialog
v-model:visible="visible"
attach="body"
placement="center"
:footer="false"
class="custom-img-dialog"
>
<template #header>
<div>请耐心等待图片生成</div>
</template>
<template #body>
<div class="custom-dialog-body">
<div class="content narrow-scrollbar">
<div v-for="(item, index) in list" :key="item.img">
<div class="img-box">
<img class="img" :src="item.img" alt="" />
<template v-if="item.split_img">
<img class="split-img img" :src="item.split_img" alt="" />
</template>
<template v-else>
<div class="split_img_loading">
<t-loading size="24px"></t-loading>
</div>
</template>
</div>
<div class="btns">
<t-button
v-for="it in btns"
:key="it.value"
@click="to_split(item.id, it.value, index)"
>{{ it.label }}</t-button
>
</div>
<div>
---------------------------------------------------------------
</div>
</div>
</div>
<Animation v-show="dialogloading"></Animation>
</div>
</template>
</t-dialog>
</template>
<script lang="ts" setup>
import {
Dialog as TDialog,
Button as TButton,
Loading as TLoading,
} from 'tdesign-vue-next';
import { ref, watch } from 'vue';
import Animation from '@/components/Animation.vue';
const btns = [
{
label: '第一张',
value: 1,
},
{
label: '第二张',
value: 2,
},
{
label: '第三张',
value: 3,
},
{
label: '第四张',
value: 4,
},
];
const props = defineProps<{
modelValue: boolean;
list: any[];
dialogloading: boolean;
}>();
const emit = defineEmits(['update:modelValue', 'SubmitSplit']);
const visible = ref(props.modelValue);
const to_split = (prompt_id: number, click_id: number, index: number) => {
if (prompt_id && click_id) {
// 发送切割图片任务
emit('SubmitSplit', {
prompt_id: prompt_id,
click_id: click_id,
index: index,
});
}
};
watch(
() => props.modelValue,
(v) => {
visible.value = v;
}
);
watch(
() => visible.value,
(v) => {
emit('update:modelValue', v);
}
);
</script>
<style lang="less">
.custom-img-dialog {
.t-dialog {
width: 50vw;
max-width: 50vw;
.t-dialog__body {
overflow: hidden;
}
}
.custom-dialog-body {
height: 500px;
box-sizing: border-box;
.content {
height: 450px;
overflow-y: auto;
.img-box {
display: flex;
justify-content: space-between;
.img {
width: 300px;
height: 300px;
}
.split_img_loading {
width: 300px;
height: 300px;
display: flex;
align-items: center;
justify-content: center;
}
}
.btns {
margin: 20px 0;
& > :not(:nth-child(1)) {
margin-left: 12px;
}
}
}
.custom-loading-box {
position: relative !important;
width: auto !important;
height: 50px !important;
}
}
}
</style>
.custom-img-to-img-content {
.keyword {
.label {
font-size: 20px;
font-weight: bold;
margin-bottom: 20px;
}
& > :nth-child(2) {
margin-bottom: 30px;
}
.t-textarea {
.t-textarea__inner {
height: 200px;
resize: none;
}
}
}
}
import { defineComponent, PropType, ref, watch } from 'vue';
import './index.less';
import { Textarea as TTextarea } from 'tdesign-vue-next';
export default defineComponent({
props: {
modelValue: {
type: String as PropType<string>,
},
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const keyword = ref('');
watch(
() => keyword.value,
(v) => {
emit('update:modelValue', v);
}
);
return () => (
<div class="custom-img-to-img-content">
<div class="keyword">
<div class="label">关键词</div>
<TTextarea autosize={false} v-model={keyword.value}></TTextarea>
</div>
</div>
);
},
});
.custom-real-upload {
margin-top: 30px;
background: #ffffff;
box-shadow: 0px 1px 6px rgba(0, 0, 0, 0.25);
border-radius: 10px;
height: 330px;
padding: 20px 46px;
position: relative;
display: flex;
justify-content: center;
flex-direction: column;
.real-upload-close-icon {
position: absolute;
right: 12px;
top: 12px;
cursor: pointer;
}
.real-upload-content {
margin-top: 6px;
display: flex;
justify-content: space-between;
.custom-real-upload-component {
width: 360px;
height: 200px;
border: 1px dashed #000000;
.t-upload {
width: 100%;
height: 100%;
.custom-upload-click-box {
border-radius: 8px;
width: 360px;
height: 200px;
display: flex;
justify-content: space-evenly;
align-items: center;
flex-direction: column;
.title {
font-weight: 600;
font-size: 18px;
color: black;
}
.title2 {
font-weight: 400;
font-size: 15px;
color: #8b8b8b;
}
.custom-chose-file {
background: #fd1753;
border-radius: 8px;
border: none;
width: 200px;
height: 46px;
--ripple-color: none !important;
}
}
.t-upload__dragger {
padding: 0;
border: none;
width: 360px;
height: 200px;
}
}
.custom-uploading-stauts {
display: flex;
justify-content: space-evenly;
align-items: center;
flex-direction: column;
width: 100%;
height: 100%;
.uploading-title {
font-weight: 400;
font-size: 15px;
color: #8b8b8b;
}
}
.custom-UploadSuccess-stauts {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 100%;
.title1 {
font-weight: 600;
font-size: 18px;
color: #000000;
}
}
}
}
.custom-real-upload-footer {
flex: 1;
display: flex;
align-items: center;
.t-button {
width: 164px;
height: 46px;
border: none;
--ripple-color: none !important;
border-radius: 8px;
font-size: 18px;
}
.submit {
background: #ebebeb;
color: #9a9a9a;
cursor: not-allowed;
}
.active {
background: #fd1753;
color: #ffffff;
cursor: pointer;
}
.reset-button {
border: 1px solid #dbdbdb;
background: white;
color: #000000;
margin-left: 12px;
}
}
}
.label {
font-size: 20px;
font-weight: bold;
margin-bottom: 20px;
}
import { computed, defineComponent, reactive, ref } from 'vue';
import './index.less';
import UploadTip from '@/assets/svg/upload/uploadTip.svg?component';
import {
MessagePlugin,
Button as TButton,
Upload as TUpload,
Progress as TProgress,
} from 'tdesign-vue-next';
import { useStore } from 'vuex';
import { getUserCookie } from '@/utils/api/userApi';
import request from '@/utils/otherRequest';
import { v4 } from 'uuid';
export default defineComponent({
props: {
modelValue: String,
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const store = useStore();
// 后台配置的地址
const adminConfigUrl = computed(() => store.getters['user/getadminConfig']);
// 上传策略
const uploadStrategy = computed(
() => store.getters['user/getuploadStrategy']
);
const files = ref([]);
// 文件地址
const Curfile = reactive({
url: '',
status: 0,
// 当前上传模块提交的状态
uploadStatus: false,
});
const actionUrl = ref('');
// 上传进度条
const percentage = ref(0);
// 定时器
let percentageInterval: any = null;
// 上传进度定时器
const openpercentage = () => {
// 开启一个定时器,模拟上传进度
percentage.value = 0;
percentageInterval = setInterval(() => {
if (percentage.value == 99) {
return;
}
percentage.value += 1;
}, 100);
};
const beforeUpload = (file: File) => {
return true;
};
const handleFail = ({ file }: any) => {
MessagePlugin.error(`文件 ${file.name} 上传失败`);
};
// 第一个上传链接
const formatResponseOne = (response: any, context: any) => {
console.log(response);
// return { name: 'FileName', url: response.url };
};
// 上传成功回调
const UploadSuccessCallback = (uuid: any, url: any) => {
// 关闭定时器
window.clearInterval(percentageInterval);
MessagePlugin.success('上传成功');
// 将将完整url传给父组件
Curfile.url = url;
// 成功2
Curfile.status = 2;
emit('update:modelValue', Curfile.url);
};
// 上传失败回调
const UploadErrorCallback = () => {
// 关闭定时器
window.clearInterval(percentageInterval);
Curfile.url = '';
// 失败0
Curfile.status = 0;
emit('update:modelValue', Curfile.url);
MessagePlugin.warning('上传失败');
};
// 内网上传-Intranet
const IntranetUpload = (file: any) => {
openpercentage();
return new Promise((resolve) => {
let uuid = v4();
// 上传中状态
Curfile.status = 1;
let url = '';
if (import.meta.env.MODE == 'production') {
// 线上地址使用完整url
url = adminConfigUrl.value + 'video/' + uuid + '.mp4';
// url = `http://192.168.1.19:5000/video/` + uuid + '.mp4';
} else if (import.meta.env.MODE == 'app') {
// app
url = '/video/' + uuid + '.mp4';
} else {
// 本地
url = '/video/' + uuid + '.mp4';
}
setTimeout(() => {
request.put(url, file[0].raw).then((res: any) => {
// resolve 参数为关键代码
if (res == 200) {
let url = adminConfigUrl.value + 'video/' + uuid + '.mp4';
UploadSuccessCallback(uuid, url);
//
Curfile.uploadStatus = true;
resolve({
status: 'success',
response: { url: Curfile.url },
});
} else {
UploadErrorCallback();
Curfile.uploadStatus = false;
}
});
}, 1000);
});
};
// 外网上传-func
const ExtranetUpload = (file: any, config: any) => {
openpercentage();
return new Promise((resolve) => {
let uuid = v4();
// 上传中状态
Curfile.status = 1;
let url = '';
const { config } = uploadStrategy.value;
url = 'https://' + config.host;
setTimeout(() => {
let formData = new FormData();
formData.append('key', config.dir + uuid + '.mp4');
formData.append('policy', config.policy);
formData.append('OSSAccessKeyId', config.accessid);
formData.append('success_action_status', '200');
formData.append('callback', config.callback);
formData.append('signature', config.signature);
// formData.append('name', uuid + '.mp4');
formData.append('file', file[0].raw);
request
.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data;charset=utf-8',
// Accept: '*/*',
},
})
.then((res: any) => {
// resolve 参数为关键代码
if (res == 200) {
// 外网url
let url = config.domain + config.dir + uuid + '.mp4';
UploadSuccessCallback(uuid, url);
//
Curfile.uploadStatus = true;
resolve({
status: 'success',
response: { url: Curfile.url },
});
} else {
UploadErrorCallback();
Curfile.uploadStatus = false;
}
})
.catch((e) => {
console.log(e);
});
}, 1000);
});
};
const requestSuccessMethod = async (file: any) => {
// if (uploadStrategy.value.oss) {
// // 外网
// return ExtranetUpload(file, uploadStrategy.value.config);
// } else {
// // 内网
// return IntranetUpload(file);
// }
//
return ExtranetUpload(file, uploadStrategy.value.config);
};
// 未上传
const notUploadHtml = () => {
return (
<TUpload
v-model={files.value}
method="PUT"
requestMethod={requestSuccessMethod}
action={actionUrl.value}
headers={{
authorization: `Bearer ${getUserCookie()}`,
}}
accept={'video'}
theme="custom"
before-upload={beforeUpload}
multiple
max={1}
draggable={true}
formatResponse={formatResponseOne}
onfail={handleFail}
>
<div class="custom-upload-click-box">
<div class="title">选择图片</div>
<div class="title2">或拖拽图片到此处</div>
<div>
<UploadTip></UploadTip>
</div>
<TButton class="custom-chose-file">选择文件</TButton>
</div>
</TUpload>
);
};
// 上传中
const UploadingHtml = () => {
return (
<div class="custom-uploading-stauts">
<TProgress
theme="circle"
percentage={percentage.value}
size={'small'}
/>
<div class="uploading-title">正在上传</div>
</div>
);
};
const UploadSuccess = () => {
return (
<div class="custom-UploadSuccess-stauts">
<div class="title1">上传完成</div>
<div class="title1">请点击下方提交按钮</div>
</div>
);
};
// 获取当前上传状态
const currentUploadStatus = () => {
if (Curfile.status == 0) {
return notUploadHtml();
} else if (Curfile.status == 1) {
// 上传中
return UploadingHtml();
} else {
// 上传完成
return UploadSuccess();
}
};
return () => (
<div>
<div class="label">原图</div>
<div class="custom-real-upload">
<div class="real-upload-content">
<div class="custom-real-upload-component">
{currentUploadStatus()}
</div>
</div>
</div>
</div>
);
},
});
.img-to-img-page {
box-sizing: border-box;
padding: 30px;
.submit-button {
background: #fd1753;
border: none;
height: 36px;
--ripple-color: none !important;
margin-top: 20px;
&:hover {
background: #fd1753;
}
}
}
import { defineComponent, onMounted, reactive, ref, watch } from 'vue';
import './index.less';
import EnterKeywords from './components/EnterKeywords';
import { useStore } from 'vuex';
import UploadImg from './components/UploadImg';
import { Button as TButton } from 'tdesign-vue-next';
import { Tasks } from '@/utils/api/Task';
import { filterRepeatTimestamp } from '@/utils/tool';
import {
useSubmitTask,
IntervalCheckTask,
SubmitSplitImgTask,
get_split_img_status,
} from '@/utils/api/userApi';
import CustomDialog from './components/Dialog/index.vue';
import Animation from '@/components/Animation.vue';
export default defineComponent({
setup(props, ctx) {
const store = useStore();
let custom_interval: any = null;
let split_interval: any = null;
const task_id = ref();
const prompt_num = ref();
const task_result_list = reactive({
list: [],
});
// 关键词
const keywords = ref('');
const loading = ref(false);
const dialogloading = ref(true);
// 图片链接
const Img_url = ref('');
// 弹窗状态
const DialogVisible = ref(false);
onMounted(() => {
store.dispatch('user/AdminConfig');
});
// 获取返回的四宫格图片
const getTask = async () => {
try {
let res: any = await IntervalCheckTask({
task_id: task_id.value,
});
if (res.code == 0 && res.data.length) {
res.data.forEach((item: any) => {
if (item.loading === undefined) {
item.loading = false;
}
});
// 合并去重
task_result_list.list = filterRepeatTimestamp(
task_result_list.list,
res.data
);
if (prompt_num.value == task_result_list.list.length) {
// 关闭定时器
closeInterval();
dialogloading.value = false;
} else {
dialogloading.value = true;
}
}
} catch (e) {
console.log(e);
}
};
// 获取返回的切割图片
const get_split_img = async (prompt_id: number) => {
try {
let res: any = await get_split_img_status({
prompt_id: prompt_id,
});
if (res.code == 0 && res.data.cut_img && res.data.id) {
// 切割成功--找到list对应的id,将cut_img添加进去
task_result_list.list.forEach((item: any) => {
if (item.id == res.data.id) {
item.split_img = res.data.cut_img;
item.loading = false;
}
});
// 关闭定时器
closeSplitTaskInterval();
}
} catch (e) {
console.log(e);
}
};
// 打开定时器
const openInterval = () => {
custom_interval = window.setInterval(() => {
getTask();
}, 3000);
};
// 打开图片切割任务定时器
const openSplitTaskInterval = (prompt_id: number) => {
split_interval = window.setInterval(() => {
get_split_img(prompt_id);
}, 3000);
};
// 关闭定时器
const closeInterval = () => {
if (custom_interval) {
window.clearInterval(custom_interval);
}
};
// 关闭图片切割任务定时器
const closeSplitTaskInterval = () => {
if (split_interval) {
window.clearInterval(split_interval);
}
};
// 提交任务
const submit = async () => {
if (!keywords.value) {
return;
}
let params: any = {
type: Img_url.value ? Tasks.img_to_img : Tasks.text_to_img,
prompt: keywords.value,
prompt_img: Img_url.value,
prompt_num: 5,
};
try {
loading.value = true;
// 请求
let res: any = await useSubmitTask(params);
if (res.code == 0) {
task_id.value = res.data.task_id;
prompt_num.value = res.data.prompt_num;
// 打开弹窗
DialogVisible.value = true;
// 开启轮询
openInterval();
}
loading.value = false;
} catch (e) {
console.log(e);
loading.value = false;
}
};
// 提交切割任务
const SubmitSplit = async ({ prompt_id, click_id, index }: any) => {
try {
let res: any = await SubmitSplitImgTask({
prompt_id: prompt_id,
cut_id: click_id,
});
if (res.code == 0) {
// 检测切割任务是否完成
openSplitTaskInterval(res.data.prompt_id);
// 将对应的item.loading打开
task_result_list.list[index].loading = true;
}
} catch (e) {
console.log(e);
}
};
return () => (
<div class="img-to-img-page">
<div class="tips">提示:可不上传图片</div>
<EnterKeywords v-model={keywords.value}></EnterKeywords>
<UploadImg v-model={Img_url.value}></UploadImg>
<TButton
onClick={submit}
class={['submit-button', !keywords.value ? 'disabled' : '']}
>
提交
</TButton>
<CustomDialog
v-model={DialogVisible.value}
list={task_result_list.list}
dialogloading={dialogloading.value}
onSubmitSplit={SubmitSplit}
></CustomDialog>
<Animation position={'fixed'} v-show={loading.value}></Animation>
</div>
);
},
});
<template>
<div class="custom-login">
<div class="custom-login-title">登录</div>
<t-form
ref="form"
class="custom-login-form"
:data="formData"
:rules="FORM_RULES"
:colon="true"
:label-width="0"
@reset="onReset"
@submit="onSubmit"
>
<t-form-item name="account">
<t-input
v-model="formData.account"
clearable
placeholder="请输入账户名"
>
<template #prefix-icon>
<desktop-icon />
</template>
</t-input>
</t-form-item>
<t-form-item name="password">
<t-input
v-model="formData.password"
type="password"
clearable
placeholder="请输入密码"
>
<template #prefix-icon>
<lock-on-icon />
</template>
</t-input>
</t-form-item>
<t-form-item>
<t-button theme="primary" type="submit" block>登录</t-button>
</t-form-item>
</t-form>
<Animation
v-show="loading"
poistion="fixed"
background="rgba(200,200,200,0.2)"
></Animation>
</div>
</template>
<script lang="ts" setup>
import { computed, reactive, ref } from 'vue';
import {
MessagePlugin,
Form as TForm,
FormItem as TFormItem,
Input as TInput,
Button as TButton,
} from 'tdesign-vue-next';
import { DesktopIcon, LockOnIcon } from 'tdesign-icons-vue-next';
import { UserLogin } from '@/utils/api/userApi';
import { useStore } from 'vuex';
import Animation from '@/components/Animation.vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const store = useStore();
const loading = ref(false);
const formData = reactive({
account: '',
password: '',
});
const FORM_RULES = computed(() => {
return {
account: [{ required: true, messgae: '账号不能为空', type: 'error' }],
password: [{ required: true, message: '密码不能为空', type: 'error' }],
};
});
const onReset = () => {
MessagePlugin.success('重置成功');
};
const onSubmit = async ({ validateResult, firstError }: any) => {
if (validateResult === true) {
try {
loading.value = true;
let res: any = await UserLogin({
email: formData.account,
password: formData.password,
});
if (res.code == 0) {
MessagePlugin.success('登录成功');
store.commit('user/setToken', {
token: res.data.access_token,
time: res.data.expires_in,
});
router.replace({
path: '/',
});
}
loading.value = false;
} catch (e) {
console.log(e);
loading.value = false;
}
} else {
MessagePlugin.closeAll();
MessagePlugin.warning(firstError);
}
};
</script>
<style lang="less">
@import '@/style/flex.less';
.custom-login {
width: 400px;
margin: 0 auto;
.dj();
flex-direction: column;
.custom-login-title {
font-weight: 500;
font-size: 50px;
color: #000000;
text-align: center;
}
.custom-login-form {
margin-top: 40px;
}
}
</style>
<template>
<div class="custom-home-page-login">
<Login></Login>
</div>
</template>
<script lang="ts" setup>
import Login from './components/login.vue';
</script>
<style lang="less">
@import '@/style/variables.less';
@import '@/style/flex.less';
.custom-home-page-login {
background: #ffffff;
height: 100%;
.dja();
flex-direction: column;
}
</style>
import router from '@/router';
import { getUserCookie } from '@/utils/api/userApi';
router.beforeEach((to: any, from: any, next: any) => {
if (to.name == 'login') {
next();
return;
}
// 用户token
let token = getUserCookie();
if (!token) {
next('/login');
return;
}
next();
});
import {
createRouter,
createWebHistory,
RouteRecordRaw,
createWebHashHistory,
} from 'vue-router';
import homeRouters from './modules/home';
// 存放固定的路由
const defaultRouterList: Array<RouteRecordRaw> = [...homeRouters];
// 打包为app时,使用hash模式
const isHistory = import.meta.env.MODE == 'app';
const router = createRouter({
history: isHistory ? createWebHashHistory() : createWebHistory(),
routes: defaultRouterList,
scrollBehavior() {
return {
el: '#app',
top: 0,
behavior: 'smooth',
};
},
});
export default router;
export default [
{
path: '/',
name: 'layout',
component: () => import('@/layout/content.vue'),
children: [
{
path: '/',
name: 'Home',
component: () => import('@/pages/ImgToImg'),
meta: {
header: true,
},
},
{
path: '/login',
name: 'login',
component: () => import('@/pages/Login/index.vue'),
meta: {
header: true,
},
},
],
},
];
import 'vue-router';
declare module '*.vue' {
import { DefineComponent } from 'vue';
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>;
export default component;
}
declare module 'vue-router' {
interface RouteMeta {
login?: boolean; //是否需要登录
title?: string; // 标题
bread?: string; //面包屑名字
}
}
declare global {
interface Window {
tvWidget: any;
}
}
import { createStore } from 'vuex';
import user from './modules/user';
export const store = createStore({
modules: {
user,
},
});
export default store;
import { TOKEN_NAME, APP_COOKIE } from '@/config/global';
import Cookies from 'js-cookie';
import { getConfigPolicy } from '@/utils/api/userApi';
interface MyState {
token: String | undefined | null;
account: number | string;
adminConfig: string;
uploadStrategy: any;
options: any[];
}
// 获取cookie
const getUserCookie = () => {
let mode = import.meta.env.MODE;
if (mode == 'app') {
// 从本地取
return window.localStorage.getItem(APP_COOKIE);
} else {
return Cookies.get(TOKEN_NAME);
}
};
// 定义的state初始值
const state: MyState = {
token: getUserCookie(),
account: '',
adminConfig: '',
uploadStrategy: {
config: {},
},
options: [],
};
type StateType = typeof state;
const mutations = {
setToken(state: StateType, obj: any) {
Cookies.set(TOKEN_NAME, obj.token, {
expires: obj.time / 60 / 60 / 24,
});
// app版
if (import.meta.env.MODE == 'app') {
window.localStorage.setItem(APP_COOKIE, obj.token);
}
state.token = obj.token;
},
removeToken(state: StateType) {
Cookies.remove(TOKEN_NAME);
state.token = '';
// app版
if (import.meta.env.MODE == 'app') {
window.localStorage.setItem(APP_COOKIE, '');
}
},
// 更新上传策略
setuploadStrategy(state: StateType, config: any) {
// 上传配置
state.uploadStrategy.config = config.config;
},
};
const getters = {
token: (state: StateType) => {
return state.token;
},
getuploadStrategy: (state: StateType) => {
return state.uploadStrategy;
},
};
const actions = {
async AdminConfig({ commit }: any) {
try {
// 请求上传配置
let result: any = await getConfigPolicy({
// 视频上传策略
id: 1,
});
if (result.code == 0) {
commit('setuploadStrategy', {
config: result.data,
});
}
} catch (e) {
console.log(e);
}
},
};
export default {
namespaced: true,
state,
mutations,
actions,
getters,
};
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
.custom-disabled-button {
height: 48px;
width: 100%;
font-weight: 600;
font-size: 17px;
color: #848e9c;
transition: all 0.3s;
}
.custom-clickable-button {
background: rgba(41, 98, 255, 0.85);
color: #ffffff;
transition: all 0.3s;
}
//默认center
.dja(@flex:center,@align:center) {
display: flex;
justify-content: @flex;
align-items: @align;
}
.dj(@flex:center) {
display: flex;
justify-content: @flex;
}
.da(@align:center) {
display: flex;
align-items: @align;
}
@import './variables.less';
body {
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
padding: 0;
margin: 0;
user-select: none;
}
ul,
dl,
li,
dd,
dt {
margin: 0;
padding: 0;
list-style: none;
}
figure,
h1,
h2,
h3,
h4,
h5,
h6,
p {
margin: 0;
}
* {
box-sizing: border-box;
}
// 页面重复的label
.custom-upload-label {
font-weight: 700;
font-size: 20px;
color: #000000;
}
*,
*:hover,
*:active,
::before,
::after {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
// * {
// -ms-touch-action: none;
// touch-action: none;
// }
// 头部导航栏高度
@navbarHeight: 6vh;
// 底部导航栏高度
@FooterHeight: 8vh;
// 页面padding
@pagepadding:0 12px;
// 动画
@anim-time-fn-easing: cubic-bezier(0.38, 0, 0.24, 1);
@anim-time-fn-ease-out: cubic-bezier(0, 0, 0.15, 1);
@anim-time-fn-ease-in: cubic-bezier(0.82, 0, 1, 0.9);
@anim-duration-base: 0.2s;
@anim-duration-moderate: 0.24s;
@anim-duration-slow: 0.28s;
export const isMobile = () => {
return navigator.userAgent.match(
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
);
};
export const openApp = (url: string) => {
let res = isMobile();
if (!res) {
return;
}
// 是手机
// window.location.href = 'https://wa.me/';
let _href = 'https://api.whatsapp.com/send?';
_href += '&text=' + encodeURIComponent(url); //标题
// _href += '&url=' + encodeURIComponent(url); //链接
window.location.href = _href;
};
export const Tasks = {
img_to_img: 1,
text_to_img: 2,
};
import request from '@/utils/request';
import store from '@/store';
// 获取cookie
export const getUserCookie = () => {
return store.getters['user/token'];
};
// 登录
export const UserLogin = (data: any) => {
return request.post('/api/users/login', {
...data,
});
};
// 获取后台配置
export const getAdminConfig = () => {
let token = getUserCookie();
return request.get('/api/users/config', {
headers: {
authorization: `Bearer ${token}`,
},
});
};
// 获取阿里云上传策略
export const getConfigPolicy = (data: any) => {
let token = getUserCookie();
return request.get('/api/users/config/policy', {
params: data,
headers: {
authorization: `Bearer ${token}`,
},
});
};
// 退出登录
export const useLogout = () => {
return request.post(
'/api/users/logout',
{},
{
headers: {
authorization: `Bearer ${getUserCookie()}`,
},
}
);
};
// 提交任务
export const useSubmitTask = (data: any) => {
return request.post(
'/api/users/submit',
{ ...data },
{
headers: {
authorization: `Bearer ${getUserCookie()}`,
},
}
);
};
// 轮询检测任务
export const IntervalCheckTask = (data: any) => {
return request.get('/api/users/task/callback', {
params: data,
headers: {
authorization: `Bearer ${getUserCookie()}`,
},
});
};
// 提交切割图片任务
export const SubmitSplitImgTask = (data: any) => {
return request.post(
'/api/users/split',
{ ...data },
{
headers: {
authorization: `Bearer ${getUserCookie()}`,
},
}
);
};
// 轮询获取图片切割任务状态
export const get_split_img_status = (data: any) => {
return request.get('/api/users/task/callback', {
params: data,
headers: {
authorization: `Bearer ${getUserCookie()}`,
},
});
};
export const getDomBounding = (dom: any) => {
let client = dom.getBoundingClientRect();
let bodyHeight = document.documentElement.clientHeight;
return {
client,
bodyHeight,
};
};
export function zipImg(file: File) {
return new Promise((resolve) => {
if (file && (file.size / 1024 > 500 || file.type !== 'image/gif')) {
let img = new Image();
img.src = URL.createObjectURL(file);
let cvs = document.createElement('canvas');
let maxRatio = 0.75; // 大图比率
let minRatio = 0.8; // 小图比率
let imgQulity = 0.2; // 图像质量
img.onload = async function () {
let ratio =
img.naturalWidth > 1000 || img.naturalHeight > 1000
? maxRatio
: minRatio;
cvs.width = img.naturalWidth * ratio;
cvs.height = img.naturalHeight * ratio;
let ctx: any = cvs.getContext('2d');
ctx.drawImage(img, 0, 0, cvs.width, cvs.height);
// 压缩后新图的 base64
let zipBase64 = cvs.toDataURL('image/jpeg', imgQulity);
let add = base642File(zipBase64);
resolve(add);
};
} else {
resolve(file);
}
});
}
// base64转图片
export function dataURLtoFile(dataurl: any, filename: any, mime: any) {
return new Promise((resolve) => {
let arr = dataurl.split(';base64,');
let bstr = atob(arr[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
let data = new File([u8arr], `${filename}`, {
type: mime,
});
resolve(data);
});
}
export function base642File(base64: any) {
const arr = base64.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
export const ChangeBlob = (value: string) => {
//将字符串 转换成 Blob 对象
let blob = new Blob([value], {
type: 'text/plain',
});
return blob;
};
export interface ColumnProps {
value: string;
label: string;
panel: any;
is_load?: boolean;
}
import axios from 'axios';
import { MessagePlugin } from 'tdesign-vue-next';
import router from '@/router';
const mode = import.meta.env.MODE;
const getBaseUrl = () => {
return '/';
};
const instance = axios.create({
// baseURL: getBaseUrl(),
timeout: 6000000,
// withCredentials: mode == 'development' ? false : true,
withCredentials: false,
});
// 请求头
instance.interceptors.request.use((config: any) => {
return config;
});
instance.interceptors.response.use(
(response) => {
const { data, status } = response;
if (status == 201 || status == 200) {
return status;
}
if (data.code === 0) {
return data;
} else {
MessagePlugin.error(data.msg || '请求错误');
return Promise.reject(data.msg);
}
},
(err) => {
console.log(err);
if ('response' in err) {
const { message: msg } = err.response.data;
if (err.response.data.indexOf('<Code>UserDisable</Code>') !== -1) {
MessagePlugin.error('阿里云可能欠费');
} else {
MessagePlugin.error(msg || '请求错误');
}
return err.response;
}
}
);
export default instance;
import {
remember_password_phone,
remember_password_email,
} from '@/constants/token';
export const getRememberList = (type: string) => {
// localStorage.setItem(remember_password_phone, JSON.stringify([]));
// localStorage.setItem(remember_password_email, JSON.stringify([]));
let new_type = '';
if (type == 'phone') {
new_type = remember_password_phone;
} else {
new_type = remember_password_email;
}
let list = localStorage.getItem(new_type);
if (list) {
return JSON.parse(list);
} else {
return [];
}
};
export const setRememberList = (obj: any, type: string) => {
let list = [obj];
let new_type = '';
if (type == 'phone') {
new_type = remember_password_phone;
} else if (type == 'email') {
new_type = remember_password_email;
}
// 本地已经存储的地址
let locaList: any = getRememberList(new_type);
if (locaList.length) {
// 判断本地是否存在相同账号
let index = locaList.findIndex((item: any) => item.account == obj.account);
if (index !== -1) {
// 找到相同的,删除
locaList.splice(index, 1);
}
list = list.concat(locaList);
}
// 存储
localStorage.setItem(new_type, JSON.stringify(list));
};
import axios from 'axios';
import { store } from '@/store/index';
import { MessagePlugin } from 'tdesign-vue-next';
import router from '@/router';
const mode = import.meta.env.MODE;
const getBaseUrl = () => {
if (mode == 'app') {
return 'http://video_publish.test';
} else {
// 打包
return '';
}
};
const instance = axios.create({
baseURL: getBaseUrl(),
timeout: 60000,
// withCredentials: mode == 'development' ? false : true,
withCredentials: mode == 'app' ? false : true,
});
// 请求头
instance.interceptors.request.use((config: any) => {
const lang = store.getters['language/getLang'];
config.headers['lang'] = lang;
return config;
});
instance.defaults.timeout = 60000;
instance.interceptors.response.use(
(response) => {
const { data } = response;
if (data.code === 0 || data.code == 201) {
return data;
} else {
MessagePlugin.error(data.msg || '请求错误');
return Promise.reject(data.msg);
}
},
(err) => {
if ('response' in err) {
const { message: msg, status_code } = err.response.data;
if (status_code == 403) {
MessagePlugin.warning('请登录');
router.replace({
path: '/',
});
return;
}
MessagePlugin.error(msg || '请求错误');
return err.response;
}
}
);
export default instance;
export const WatchColor = (value: string) => {
try {
if (value[0] === '-') {
return '#F05451';
} else {
return '#25A69A';
}
} catch (e) {
return '#25A69A';
}
};
/*
* @Author: walker.liu
* @Date: 2022-05-24 10:51:56
* @Copyright(C): 2019-2020 ZP Inc. All rights reserved.
*/
import store from '@/store';
import request from '@/utils/request';
import { PriceAccuracy, computedTime } from './tool';
declare const TradingView: any;
/**
* @key Server ding字段 supported_resolutions
*/
// 1min, 5min, 15min, 30min, 60min, 4hour, 1day, 1mon, 1week, 1year
export const intervalMap = {
'1min': '1',
'5min': '5',
'15min': '15',
'30min': '30',
'60min': '60',
'4hour': '240',
'1day': 'D',
'1week': 'W',
'1mon': 'M',
};
/** trading-view 的時間區間 */
export const supportedResolutions = [
'1',
'5',
'15',
'30',
'60',
'240',
'D',
'W',
'M',
];
export class DataFeed {
private symbolInfo: any;
private subscribers: any[] = [];
private interval: any = null;
private resolution = '1';
// 每个时间段都要给5次重试次数
private RetryTime: any = {
r1: 0,
r5: 0,
r15: 0,
r60: 0,
r240: 0,
r1D: 0,
};
private KPirce: any = null;
constructor(symbolInfo: any, timeInterval = 1000) {
this.KPirce = symbolInfo.KPirce;
// 将传过来的多余参数剔除
delete symbolInfo.KPirce;
this.symbolInfo = symbolInfo;
this.intervalGetBars(symbolInfo.ticker, timeInterval);
}
public onReady(onGameLoad: any) {
new Promise((resolve) => {
resolve(void 0);
}).then(() => {
onGameLoad({
supported_resolutions: supportedResolutions,
});
});
}
public searchSymbols() {}
public resolveSymbol(
symbolName: any,
onSymbolResolvedCallback: any,
onResolveErrorCallback: any
) {
new Promise((resolve) => {
resolve(void 0);
}).then(() => {
let PriceNum = 10000000000;
PriceNum = PriceAccuracy(this.KPirce);
onSymbolResolvedCallback({
session: '24x7',
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
supported_resolutions: supportedResolutions,
has_intraday: true,
has_daily: true,
// has_no_volume: true,
minmov: 1,
// minmove2:0,
// 最多支持16位小数
pricescale: PriceNum,
...this.symbolInfo,
});
});
}
/**
* 订阅K线数据。图表库将调用onRealtimeCallback方法以更新实时数据
*/
public subscribeBars(
symbolInfo: any,
resolution: string,
onRealtimeCallback: any,
subscriberUID: any,
onResetCacheNeededCallback: () => void
) {
if (this.subscribers[subscriberUID]) {
return;
}
this.resolution = resolution;
this.subscribers[subscriberUID] = {
lastBarTime: null,
listener: onRealtimeCallback,
resolution: resolution,
symbolInfo: symbolInfo,
};
}
/**
* 取消订阅K线数据
*/
public unsubscribeBars(subscriberUID: any) {
if (!this.subscribers[subscriberUID]) {
return;
}
delete this.subscribers[subscriberUID];
}
public updateKLine(bar: any) {
for (const listenerGuid in this.subscribers) {
const subscriptionRecord = this.subscribers[listenerGuid];
if (
subscriptionRecord.lastBarTime !== null &&
bar.time < subscriptionRecord.lastBarTime
) {
continue;
}
subscriptionRecord.lastBarTime = bar.time;
subscriptionRecord.listener(bar);
}
}
public async getBars(
symbolInfo: any,
resolution: any,
periodParams: any,
onHistoryCallback: any,
onErrorCallback: any
) {
try {
const token = store.getters['user/token'];
let params = {
...periodParams,
resolution,
symbol: symbolInfo.ticker,
};
params.from = params.from * 1000;
params.to = params.to * 1000;
// 是否第一次請求歷史數據
if (periodParams.firstDataRequest) {
params.to = Date.now();
params.is_first = true;
// 添加limit
params.limit = params.countBack + 50;
delete params.firstDataRequest;
delete params.countBack;
} else {
params.is_first = false;
delete params.firstDataRequest;
// 添加limit
params.limit = params.countBack;
delete params.countBack;
}
request
.get(`/api/currencies/kline`, {
params: params,
headers: {
authorization: `Bearer ${token}`,
},
})
.then((res: any) => {
let result = {
bars: [],
meta: { noData: false },
};
if (
res.data.list &&
res.data.list.length > 0 &&
this.RetryTime[`r${resolution}`] < 5
) {
result.meta.noData = false;
result.bars = res.data.list.map((item: any) => {
return {
time: item.ts,
open: item.open,
high: item.high,
low: item.low,
close: item.close,
volume: item.volume,
};
});
store.commit(
'token/setTradePrice',
res.data.list[res.data.list.length - 1].close
);
} else {
if (this.RetryTime[`r${resolution}`] < 5) {
this.RetryTime[`r${resolution}`] += 1;
} else {
result.meta.noData = true;
}
}
onHistoryCallback(result.bars, result.meta);
});
} catch (error) {
onHistoryCallback({
bars: [],
meta: { noData: true },
});
}
}
public intervalGetBars(symbol: any, timeInterval: number) {
this.interval = setInterval(() => {
const token = store.getters['user/token'];
let from = computedTime(this.resolution);
let current = new Date().getTime();
let params = {
resolution: this.resolution,
to: current,
// from: current - timeInterval,
from: from ? from : current - timeInterval,
// from: current - 5000,
symbol: symbol,
is_first: false,
limit: 2,
};
request
.get(`/api/currencies/kline`, {
params: params,
headers: {
authorization: `Bearer ${token}`,
},
})
.then((res: any) => {
if (res && res.data.list.length) {
res.data.list.forEach((item: any) => {
this.updateKLine({
time: item.ts,
open: item.open,
high: item.high,
low: item.low,
close: item.close,
volume: item.volume,
});
});
store.commit(
'token/setTradePrice',
res.data.list[res.data.list.length - 1].close
);
}
});
}, 5000);
}
public clearIntervalGetBars() {
window.clearInterval(this.interval);
}
}
export class Widget {
private options: any;
public widget = null;
constructor(options = {}) {
const mode = store.getters['theme/getTheme'];
// 语言
const language = store.getters['language/getLang'];
// 获取当前语言
let CurLanguage = 'zh';
if (language == 'cn') {
CurLanguage = 'zh';
} else if (language == 'es') {
CurLanguage = 'es';
} else if (language == 'en') {
CurLanguage = 'en';
}
let VITE_MODE = import.meta.env.MODE;
this.options = {
// debug: true,
library_path:
VITE_MODE == 'app' ? './charting_library/' : '/charting_library/',
// 主题
theme: mode,
locale: CurLanguage,
autosize: 1,
container: 'tv_chart_container',
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
interval: '1',
// toolbar_bg: '#ffffff', //左侧工具栏背景色
favorites: {
intervals: ['1', '5', '15', '60', '240', 'D', 'W', 'M'],
chartTypes: ['Candles', 'Line'],
},
// enabled_features: ['study_templates'],
disabled_features: [
'header_symbol_search',
'left_toolbar', // 隐藏左侧工具栏
'timeframes_toolbar', //底部tab
'header_compare',
'header_saveload',
'header_fullscreen_button',
'header_screenshot', //截图相机
'vert_touch_drag_scroll', //垂直移动
// 禁用本地存储功能
// 'use_localstorage_for_settings',
'save_chart_properties_to_local_storage',
],
overrides: {
// 默认展示的图表
'mainSeriesProperties.style': 1,
// 'mainSeriesProperties.highLowAvgPrice.highLowPriceLinesVisible': true, //高低线值
// 'mainSeriesProperties.highLowAvgPrice.highLowPriceLabelsVisible': true, //高低线名称
// 'mainSeriesProperties.highLowAvgPrice.averageClosePriceLineVisible':
// true, //均线
// 'mainSeriesProperties.highLowAvgPrice.averageClosePriceLabelVisible':
// true, //均线
},
custom_css_url:
VITE_MODE == 'app'
? './src/style/tradingview.less'
: '/src/style/tradingview.less',
...options,
};
this.initWidget();
}
public initWidget() {
try {
this.widget = window.tvWidget = new TradingView.widget(this.options);
} catch (e) {
console.log(e);
console.log('TradingView--bug');
}
}
}
export function updateProgress(event: any) {
if (event.lengthComputable) {
var percentComplete = event.loaded / event.total;
}
}
export function xhr() {
var xhr = new XMLHttpRequest();
return {
request: (method: any, url: any, data: any, success: any, err: any) => {
xhr.open(method, url);
xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;
if (method == 'GET') {
xhr.send();
} else {
xhr.setRequestHeader(
'Content-Type',
'application/x-www-form-urlencoded'
);
// xhr.setRequestHeader('test', 'test');
// xhr.setRequestHeader('Host', '192.168.1.1:5000');
// xhr.setRequestHeader('Origin', 'http://192.168.1.1:5000');
// xhr.setRequestHeader('Referer', 'http://192.168.1.1:5000/upload');
// 声明请求源
xhr.setRequestHeader('Origin', 'http://a.example.com');
xhr.send(data);
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
success(xhr.responseText);
} else {
err();
}
};
},
};
}
/// <reference types="vite/client" />
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"baseUrl": "./",
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true,
"paths": {
"@/*": ["src/*"]
},
"types": [
// 一定要声明
"vite-svg-loader"
]
},
"include": [
"**/*.ts",
"src/**/*.d.ts",
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue"
],
"exclude": ["node_modules", "public"],
"references": [{ "path": "./tsconfig.node.json" }]
}
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
import { defineConfig } from 'vite';
import createVuePlugin from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import svgLoader from 'vite-svg-loader';
import viteCompression from 'vite-plugin-compression';
import path from 'path';
import legacy from '@vitejs/plugin-legacy';
// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
// 获取打包时间--
let date = new Date();
let newDate = `${date.getFullYear()}-${
date.getMonth() + 1
}-${date.getDate()}--${date.getHours()}.${date.getMinutes()}`;
let api = 0 ? 'http://42.194.143.229:90' : 'http://gpt.test';
return {
base: '/',
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
// web3: 'web3/dist/web3.min.js',
},
},
server: {
port: 3008,
host: '0.0.0.0',
proxy: {
'/api': api,
'/video': 'http://192.168.1.19:5000',
'/files': {
target: 'https://chensav.oss-cn-shenzhen.aliyuncs.com', // 代理的目标地址
changeOrigin: true, // 开发模式,默认的origin是真实的 origin:localhost:3000 代理服务会把origin修改为目标地址
// secure: true, // 是否https接口
// ws: true, // 是否代理websockets
rewrite: (path) => path.replace(/^\/files/, ''),
},
},
},
plugins: [
createVuePlugin(),
vueJsx(),
svgLoader(),
viteCompression(),
legacy({
targets: ['defaults', 'not IE 11'],
}),
],
build: {
minify: 'terser', // 混淆器,terser构建后文件体积更小
outDir: mode != 'app' ? `GPT-AI-${newDate}` : 'dist', //指定输出路径
cssCodeSplit: false, // 如果设置为false,整个项目中的所有 CSS 将被提取到一个 CSS 文件中
terserOptions: {
compress: {
//生产环境时移除console
drop_console: true,
// drop_debugger: true,
},
output: {
// 去掉注释内容
comments: false,
},
},
// http-server -P http://142.194.143.229
// target: 'es2015',
rollupOptions: {
output: {
manualChunks: {
// 拆分代码,这个就是分包,配置完后自动按需加载,现在还比不上webpack的splitchunk,不过也能用了。
vue: ['vue', 'vue-router', 'vuex'],
},
},
},
},
};
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment