Commit 7bdfd5aa authored by 朱国瑞's avatar 朱国瑞

登录校验和裁剪视频功能

parent dd2526f1
VITE_APP_BASE_URL=http://se-test.wmdigit.com
\ No newline at end of file
......@@ -5,5 +5,5 @@
// Generated by unplugin-auto-import
export {}
declare global {
const ElMessage: typeof import('element-plus/es')['ElMessage']
}
......@@ -33,9 +33,11 @@ declare module 'vue' {
IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
LoginDialog: typeof import('./src/components/LoginDialog.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
VideoCrop: typeof import('./src/components/VideoCrop.vue')['default']
WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
}
export interface ComponentCustomProperties {
......
......@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>text2video</title>
</head>
<body>
......
......@@ -11,12 +11,14 @@
"@element-plus/icons-vue": "^2.1.0",
"axios": "^1.6.1",
"bootstrap": "^5.3.3",
"cropperjs": "^1.6.2",
"crypto-js": "^4.2.0",
"element-plus": "^2.4.2",
"file-saver": "^2.0.5",
"pinia": "^2.1.7",
"sass": "^1.69.5",
"vue": "^3.3.4",
"vue-drag-resize": "^2.0.3",
"vue-router": "^4.2.5"
},
"devDependencies": {
......@@ -1267,6 +1269,11 @@
"dev": true,
"license": "MIT"
},
"node_modules/cropperjs": {
"version": "1.6.2",
"resolved": "https://registry.npmmirror.com/cropperjs/-/cropperjs-1.6.2.tgz",
"integrity": "sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA=="
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
......@@ -4168,6 +4175,11 @@
}
}
},
"node_modules/vue-drag-resize": {
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/vue-drag-resize/-/vue-drag-resize-2.0.3.tgz",
"integrity": "sha512-5q03tZ/LyvQsg1iHRcqs+wI2OKNbNIWl9+7V8rVL6MxJhZLCIYSSgbAUaDE38LhD6dFd5aJhdgNmES61AxjXuw=="
},
"node_modules/vue-eslint-parser": {
"version": "9.3.2",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz",
......
......@@ -15,12 +15,14 @@
"@element-plus/icons-vue": "^2.1.0",
"axios": "^1.6.1",
"bootstrap": "^5.3.3",
"cropperjs": "^1.6.2",
"crypto-js": "^4.2.0",
"element-plus": "^2.4.2",
"file-saver": "^2.0.5",
"pinia": "^2.1.7",
"sass": "^1.69.5",
"vue": "^3.3.4",
"vue-drag-resize": "^2.0.3",
"vue-router": "^4.2.5"
},
"devDependencies": {
......
<script setup lang="ts">
import { RouterLink, RouterView } from "vue-router";
import { ElConfigProvider } from "element-plus";
import zhCn from "element-plus/dist/locale/zh-cn.mjs";
const locale = zhCn;
import { RouterLink, RouterView } from 'vue-router'
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
const locale = zhCn
</script>
<template>
......
import { getToken } from '@/utils/token'
import axios from 'axios'
import { ElLoading } from 'element-plus'
import { ElLoading, ElMessage } from 'element-plus'
const VERSION = '20210531'
......@@ -40,7 +41,18 @@ function isHideLoading() {
request.interceptors.request.use(
(config: any) => {
config.headers['tenant'] = '0'
// permission 处理
if (config.url === 'user/refresh') {
const refreshToken = getToken('refresh_token')
if (refreshToken) {
config.headers['Authorization'] = refreshToken
}
} else {
const accessToken = getToken('access_token')
if (accessToken) {
config.headers['Authorization'] = accessToken
}
}
if (!isHideLoading()) {
loading = ElLoading.service({ fullscreen: true })
}
......
This diff is collapsed.
/**
* 用户相关接口
*/
import request from '@/api/request'
import { useAuthStore } from '@/stores/state'
import { saveTokens } from '@/utils/token'
export default {
// 登录
login(postData: Wm.LoginParams) {
return new Promise((resolve, reject) => {
request
.post('/user/login', postData)
.then((res: any) => {
const authStore = useAuthStore()
authStore.login()
saveTokens(res.access_token, res.refresh_token)
resolve(res)
})
.catch((err: any) => {
reject(err)
})
})
},
// 注册
register(postData: Wm.RegisterParams) {
return request.post('/user/login', postData)
}
}
<script lang="ts" setup>
import userService from '@/api/service/userService'
import utils from '@/utils/utils'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { onMounted, reactive, ref, watch, watchEffect } from 'vue'
const props = defineProps(['visible'])
const emit = defineEmits(['update:visible'])
const isVisible = ref(props.visible)
const loginFormRef = ref<FormInstance>()
const title = ref('登录')
const isRegister = ref(false)
const isMobile = ref(utils.checkIsMobile())
const loginForm = reactive({
username: '',
password: '',
captcha: ''
})
const rules = reactive<FormRules<typeof loginForm>>({
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
captcha: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
})
onMounted(() => {
console.log('mounted')
})
watch(
() => props.visible,
(val) => {
console.log('visible', val)
isVisible.value = val
}
)
const onCancel = () => {
console.log('cancel')
emit('update:visible', false)
}
const handleSwitch = () => {
console.log('handleSwitchRegister')
isRegister.value = !isRegister.value
if (isRegister.value) {
title.value = '注册'
} else {
title.value = '登录'
}
}
const onConfirm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
const postData = {
username: loginForm.username,
password: loginForm.password
}
userService
.login(postData)
.then((res: any) => {
console.log('res', res)
ElMessage.success({
message: '登录成功'
})
handleClose()
})
.catch((err: any) => {
console.log('err', err)
ElMessage.error({
message: err.message
})
})
} else {
console.log('error submit!', fields)
}
})
}
const handleClose = () => {
console.log('handleClose')
emit('update:visible', false)
}
</script>
<template>
<el-dialog
v-model="isVisible"
lock-scroll
:close-on-click-modal="false"
:show-close="false"
:title="title"
:width="isMobile ? '80%' : '400px'"
align-center
center
class="wm-login-dialog"
:class="{ 'is-mobile': isMobile }"
>
<el-form
ref="loginFormRef"
style="max-width: 400px"
:model="loginForm"
status-icon
:rules="rules"
label-width="auto"
class="wm-login-form"
>
<el-form-item label="用户名" prop="username">
<el-input class="wm-input" v-model="loginForm.username" type="text" autocomplete="off" />
</el-form-item>
<el-form-item label="密码 " prop="password">
<el-input
class="wm-input"
v-model="loginForm.password"
type="password"
autocomplete="off"
/>
</el-form-item>
<!-- <el-form-item label="验证码" prop="captcha">
<el-input v-model.number="loginForm.captcha" />
</el-form-item> -->
<!-- <div class="register-tips">
<span class="link" @click="handleSwitch">{{ isRegister ? '去登录' : '去注册' }}</span>
</div> -->
</el-form>
<template #footer>
<div class="dialog-footer">
<!-- <el-button @click="onCancel">Cancel</el-button> -->
<el-button type="primary" class="login-btn" @click="onConfirm(loginFormRef)">
登录
</el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped lang="scss">
.wm-login-dialog {
.wm-login-form {
.register-tips {
text-align: right;
.link {
color: #409eff;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
}
}
.login-btn {
width: 100%;
}
.wm-input {
width: 300px;
}
&.is-mobile {
.wm-input {
width: 100%;
}
}
}
</style>
<style lang="scss">
.wm-login-dialog {
.el-dialog__body {
padding: 10px calc(var(--el-dialog-padding-primary) + 5px) 10px;
}
}
</style>
<script lang="ts" setup>
import userService from '@/api/service/userService'
import utils from '@/utils/utils'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { nextTick, onMounted, reactive, ref, toRaw, watch, watchEffect } from 'vue'
import VueDragResize from 'vue-drag-resize'
const props = defineProps({
visible: {
type: Boolean,
default: false
},
url: {
type: String,
default: ''
},
cropArea: {
type: Array,
default: () => []
}
})
watch(
() => [props.cropArea, props.visible],
(newVal, oldVal) => {
console.log('cropArea', newVal[0])
if (!props.visible) {
showCropBox.value = false
// handleInitCropArea()
}
}
)
const emit = defineEmits(['update:visible', 'crop'])
const isVisible = ref(props.visible)
const loginFormRef = ref<FormInstance>()
const title = ref('裁剪视频')
const isMobile = ref(utils.checkIsMobile())
const cropBoxRef = ref<any>()
const backgroundVideoRef = ref<HTMLVideoElement>()
const croppedCanvasRef = ref<HTMLCanvasElement>()
const rectData = ref({
left: 0,
top: 0,
height: 100,
width: 100
})
const showCropBox = ref(false)
const cropPreviewBox = ref({
width: 0,
height: 0
})
onMounted(() => {
console.log('mounted')
})
watch(
() => props.visible,
(val) => {
console.log('visible', val)
isVisible.value = val
}
)
const onCancel = () => {
console.log('cancel')
handleClose()
}
const onConfirm = async () => {
const video = backgroundVideoRef.value
// 计算裁剪框在视频中的位置
const scaleX = video!.videoWidth / video!.clientWidth
const scaleY = video!.videoHeight / video!.clientHeight
const cropData = {
x: Math.round(rectData.value.left * scaleX),
y: Math.round(rectData.value.top * scaleY),
width: Math.round(rectData.value.width * scaleX),
height: Math.round(rectData.value.height * scaleY)
}
if (cropData.width <= 0 || cropData.height <= 0) {
ElMessage.error('裁剪框尺寸不正确')
return
}
if (cropData.x < 0) {
cropData.x = 0
}
if (cropData.y < 0) {
cropData.y = 0
}
if (cropData.width > video!.videoWidth) {
cropData.width = video!.videoWidth
}
if (cropData.x + cropData.width > video!.videoWidth) {
cropData.width = video!.videoWidth - cropData.x
}
if (cropData.height > video!.videoHeight) {
cropData.height = video!.videoHeight
}
if (cropData.y + cropData.height > video!.videoHeight) {
cropData.height = video!.videoHeight - cropData.y
}
console.log(rectData.value, scaleX, scaleY)
console.log('cropData', cropData)
// 转换为 左上角坐标 右下角坐标 例如:[[100,200], [300,400]]
const cropAreaData = [
[cropData.x, cropData.y],
[cropData.x + cropData.width, cropData.y + cropData.height]
]
emit('crop', cropAreaData)
handleClose()
}
const handleClose = () => {
console.log('handleClose')
emit('update:visible', false)
}
const cropVideoFrame = () => {
try {
const cropBox = cropBoxRef.value.$el.getBoundingClientRect()
const video = backgroundVideoRef.value
const canvas = croppedCanvasRef.value
if (!video || !canvas) {
return
}
const ctx = canvas!.getContext('2d')
const scaleX = video!.videoWidth / video!.clientWidth
const scaleY = video!.videoHeight / video!.clientHeight
canvas!.width = cropBox.width
canvas!.height = cropBox.height
ctx!.drawImage(
video!,
(cropBox.left - video!.getBoundingClientRect().left) * scaleX,
(cropBox.top - video!.getBoundingClientRect().top) * scaleY,
cropBox.width * scaleX,
cropBox.height * scaleY,
0,
0,
cropBox.width,
cropBox.height
)
// 递归调用,实现每一帧都更新
requestAnimationFrame(cropVideoFrame)
} catch (error) {
console.log(error)
}
}
const resize = (newRect: any) => {
// console.log(newRect)
rectData.value = newRect
}
const onVideoLoaded = (val: any) => {
console.log('onVideoLoaded', val)
const video = backgroundVideoRef.value
cropPreviewBox.value = {
width: video?.clientWidth || 0,
height: video?.clientHeight || 0
}
showCropBox.value = true
const cropArea: any = toRaw(props.cropArea)
if (cropArea.length > 0) {
const scaleX = cropPreviewBox.value.width / video!.videoWidth
const scaleY = cropPreviewBox.value.height / video!.videoHeight
console.log('cropArea', cropArea, scaleX, scaleY)
const left = Math.round(cropArea[0][0] * scaleX)
const top = Math.round(cropArea[0][1] * scaleY)
const width = Math.round((cropArea[1][0] - cropArea[0][0]) * scaleX)
const height = Math.round((cropArea[1][1] - cropArea[0][1]) * scaleY)
console.log('init cropArea', left, top, width, height)
rectData.value = {
left,
top,
width,
height
}
} else {
// 全部显示
rectData.value = {
left: 0,
top: 0,
width: video!.clientWidth,
height: video!.clientHeight
}
}
// nextTick(() => {
// cropVideoFrame()
// })
}
const handleInitCropArea = () => {
const video = backgroundVideoRef.value
const cropArea: any = toRaw(props.cropArea)
if (!video) {
return
}
if (cropArea.length > 0) {
const scaleX = cropPreviewBox.value.width / video!.videoWidth
const scaleY = cropPreviewBox.value.height / video!.videoHeight
console.log('cropArea', cropArea, scaleX, scaleY)
const left = Math.round(cropArea[0][0] * scaleX)
const top = Math.round(cropArea[0][1] * scaleY)
const width = Math.round((cropArea[1][0] - cropArea[0][0]) * scaleX)
const height = Math.round((cropArea[1][1] - cropArea[0][1]) * scaleY)
console.log('init cropArea', left, top, width, height)
rectData.value = {
left,
top,
width,
height
}
}
}
</script>
<template>
<el-dialog
v-model="isVisible"
:close-on-click-modal="false"
:show-close="false"
:title="title"
:width="isMobile ? '80%' : '400px'"
align-center
center
lock-scroll
class="wm-video-crop-dialog"
:class="{ 'is-mobile': isMobile }"
>
<!-- 底部视频和裁剪框 -->
<div class="video-crop-container">
<video
ref="backgroundVideoRef"
:src="url"
autoplay
loop
muted
webkit-playsinline
playsinline
class="background-video"
@loadeddata="onVideoLoaded"
v-if="isVisible"
></video>
<VueDragResize
ref="cropBoxRef"
:w="rectData.width"
:h="rectData.height"
:x="rectData.left"
:y="rectData.top"
:parent-limitation="true"
:is-resizable="true"
:is-draggable="true"
class="crop-box"
v-on:resizing="resize"
v-on:dragging="resize"
:isActive="true"
v-if="showCropBox"
></VueDragResize>
</div>
<!-- 显示裁剪后的内容 -->
<div class="cropped-image-container" v-if="false">
<div class="title">裁剪后的视频显示效果</div>
<div
:style="{
width: cropPreviewBox.width + 'px',
height: cropPreviewBox.height + 'px'
}"
>
<canvas ref="croppedCanvasRef"></canvas>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button class="btn" @click="onCancel">取消</el-button>
<el-button type="primary" class="btn confirm" @click="onConfirm"> 裁剪 </el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped lang="scss">
.wm-video-crop-dialog {
.video-crop-container {
position: relative;
width: 100%;
font-size: 0;
.background-video {
width: 100%;
height: 100%;
object-fit: cover;
}
.crop-box {
border-color: var(--el-color-primary-dark-2);
position: absolute;
cursor: move;
.vdr-stick {
background: var(--el-color-primary-dark-2);
border: 1px solid var(--el-color-primary-dark-2);
-webkit-box-shadow: 0 0 2px var(--el-color-primary-dark-2);
box-shadow: 0 0 2px var(--el-color-primary-dark-2);
}
}
}
.cropped-image-container {
margin-top: 10px;
// text-align: center;
.title {
font-size: 16px;
margin-bottom: 10px;
}
}
.brn {
&.confirm {
margin-left: 10px;
}
}
&.is-mobile {
.wm-input {
width: 100%;
}
}
}
</style>
<style lang="scss">
.wm-video-crop-dialog {
.el-dialog__body {
padding: 10px calc(var(--el-dialog-padding-primary) + 5px) 10px;
}
.video-crop-container {
.crop-box {
border-color: var(--el-color-primary);
&::before {
outline: 1px dashed var(--el-color-primary);
}
.vdr-stick {
background: var(--el-color-primary);
border: 1px solid var(--el-color-primary);
-webkit-box-shadow: 0 0 2px var(--el-color-primary);
box-shadow: 0 0 2px var(--el-color-primary);
}
}
}
}
</style>
......@@ -7,6 +7,7 @@ import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import VueDragResize from 'vue-drag-resize'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
......@@ -15,5 +16,6 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.use(createPinia())
app.use(router)
app.use(ElementPlus)
app.use(VueDragResize)
app.mount('#app')
......@@ -5,7 +5,6 @@ import RecordSteps from '../views/home/record_steps.vue'
import CalcRetire from '../views/home/calc_retire.vue'
import GenDigitHumanVideo from '../views/home/gen_digit_human_video.vue'
const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL),
routes: [
......
import { getToken } from '@/utils/token'
import { defineStore } from 'pinia'
export const useAuthStore = defineStore('auth', {
state: () => ({
isLoggedIn: getToken('access_token') ? true : false
}),
actions: {
login() {
this.isLoggedIn = true
},
logout() {
this.isLoggedIn = false
}
}
})
/**
* 存储tokens
* @param {string} accessToken
* @param {string} refreshToken
*/
export function saveTokens(accessToken: string, refreshToken: string) {
localStorage.setItem('access_token', `Bearer ${accessToken}`)
localStorage.setItem('refresh_token', `Bearer ${refreshToken}`)
}
/**
* 存储access_token
* @param {string} accessToken
*/
export function saveAccessToken(accessToken: string) {
localStorage.setItem('access_token', `Bearer ${accessToken}`)
}
/**
* 获得某个token
* @param {string} tokenKey
*/
export function getToken(tokenKey: string) {
return localStorage.getItem(tokenKey)
}
/**
* 移除token
*/
export function removeToken() {
localStorage.removeItem('access_token')
localStorage.removeItem('refresh_token')
}
import _cryptoJs from 'crypto-js';
import _cryptoJs from 'crypto-js'
export default class utils {
// 格式化为json字符串
static formatJson(jsonString: string) {
try {
return JSON.stringify(JSON.parse(jsonString), null, 2); // 对JSON字符串进行格式化处理
return JSON.stringify(JSON.parse(jsonString), null, 2) // 对JSON字符串进行格式化处理
} catch (error) {
return 'Invalid JSON'
}
......@@ -22,22 +22,22 @@ export default class utils {
// 从文本中提取 JSON
static extractJSON(text: string) {
// 正则表达式匹配 JSON 格式的字符串
const jsonRegex = /\[\s*\{\s*"序号"\s*:\s*\d+.*?\}\s*\]/s;
const matches = text.match(jsonRegex);
return matches ? matches[0] : null;
const jsonRegex = /\[\s*\{\s*"序号"\s*:\s*\d+.*?\}\s*\]/s
const matches = text.match(jsonRegex)
return matches ? matches[0] : null
}
// 生成年月日时分秒毫秒字符串
static genDateTimeStr() {
const now = new Date();
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const day = now.getDate().toString().padStart(2, '0');
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const seconds = now.getSeconds().toString().padStart(2, '0');
const milliseconds = now.getMilliseconds().toString().padStart(3, '0');
const formattedDateTime = `${year}${month}${day}${hours}${minutes}${seconds}${milliseconds}`;
const now = new Date()
const year = now.getFullYear()
const month = (now.getMonth() + 1).toString().padStart(2, '0')
const day = now.getDate().toString().padStart(2, '0')
const hours = now.getHours().toString().padStart(2, '0')
const minutes = now.getMinutes().toString().padStart(2, '0')
const seconds = now.getSeconds().toString().padStart(2, '0')
const milliseconds = now.getMilliseconds().toString().padStart(3, '0')
const formattedDateTime = `${year}${month}${day}${hours}${minutes}${seconds}${milliseconds}`
// console.log(formattedDateTime); // 输出类似:20221231120530123
return formattedDateTime
}
......@@ -45,39 +45,41 @@ export default class utils {
// 对一句话再次拆分文本
static splitDetailText(sentences: string[]) {
// console.log(sentences)
let result_sentences: string[] = [];
let currentSentence = '';
let result_sentences: string[] = []
let currentSentence = ''
for (let i = 0; i < sentences.length; i++) {
const str = sentences[i];
currentSentence += str + ',';
const str = sentences[i]
currentSentence += str + ','
if (i < sentences.length - 1 && (currentSentence + sentences[i + 1]).length <= 20) {
continue;
}
if (i === sentences.length - 2 && sentences[i + 1].length <= 5) {
continue;
continue
}
if (i === sentences.length - 2 && sentences[i + 1].length <= 5) {
continue
}
if (currentSentence.length < 10) {
continue;
continue
}
result_sentences.push(currentSentence.endsWith(",") ? currentSentence.slice(0, -1) : currentSentence);
currentSentence = '';
result_sentences.push(
currentSentence.endsWith(',') ? currentSentence.slice(0, -1) : currentSentence
)
currentSentence = ''
}
// console.log('result_sentences=', result_sentences);
return result_sentences;
return result_sentences
}
static splitMoreText (sentences: string[]) {
static splitMoreText(sentences: string[]) {
// console.log(sentences)
let result_sentences: string[] = [];
let result_sentences: string[] = []
for (let i = 0; i < sentences.length; i++) {
const str = sentences[i];
let tempSentences = str.split(/[!|?|。|!|?|,|,]/);
let newList = utils.splitDetailText(tempSentences);
result_sentences = result_sentences.concat(newList);
const str = sentences[i]
let tempSentences = str.split(/[!|?|。|!|?|,|,]/)
let newList = utils.splitDetailText(tempSentences)
result_sentences = result_sentences.concat(newList)
}
// console.log('result_sentences=', result_sentences);
return result_sentences;
};
return result_sentences
}
// 拆分文本
static splitText(str: string, type: string = 'default') {
......@@ -98,37 +100,47 @@ export default class utils {
// 拆分英文文本
static splitTextEn(str: string) {
str = str.replaceAll('"','').replaceAll('"','')
str = str.replaceAll('"', '').replaceAll('"', '')
// 使用正则表达式拆分文本
let sentences = str.split(/[!|?|.]/);
let sentences = str.split(/[!|?|.]/)
// 过滤掉长度为 0 的句子
sentences = sentences.filter(s => s.length > 0);
sentences = sentences.filter((s) => s.length > 0)
// console.log(sentences)
return sentences
}
// 过滤掉中文字符
static filterChineseAndPunctuation(inputString: string) {
return inputString.replace(/[\u4E00-\u9FA5\u3000-\u303F\uff00-\uffef]/g, '') // 过滤中文字符
.replace(/[^\w\s]|_/g, '') // 过滤标点符号
.replace(/\s+/g, ' '); // 连续多个空格替换为一个空格
return inputString
.replace(/[\u4E00-\u9FA5\u3000-\u303F\uff00-\uffef]/g, '') // 过滤中文字符
.replace(/[^\w\s]|_/g, '') // 过滤标点符号
.replace(/\s+/g, ' ') // 连续多个空格替换为一个空格
}
// 检查该字符串是否只包含中文标点符号和英文标点符号
static containsOnlyPunctuation(str: string) {
// 使用正则表达式匹配是否只包含标点符号(包括中文标点)
return /^[!-\/:-@\[-`{-~\p{P}\p{S}\s]*$/u.test(str);
return /^[!-\/:-@\[-`{-~\p{P}\p{S}\s]*$/u.test(str)
}
// 加密
static aesEncrypt(word: string) {
var key = _cryptoJs.enc.Utf8.parse('e6ef616dc57343248f6b3e98a07e1dde');
var srcs = _cryptoJs.enc.Utf8.parse(word);
var key = _cryptoJs.enc.Utf8.parse('e6ef616dc57343248f6b3e98a07e1dde')
var srcs = _cryptoJs.enc.Utf8.parse(word)
var encrypted = _cryptoJs.AES.encrypt(srcs, key, {
mode: _cryptoJs.mode.ECB,
padding: _cryptoJs.pad.Pkcs7
});
return encrypted.toString();
})
return encrypted.toString()
}
static checkIsMobile = () => {
const userAgent = navigator.userAgent || navigator.vendor
if (/Mobile|Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent)) {
return true
} else {
return false
}
}
}
<script setup lang="ts">
import text2videoService from "@/api/service/text2videoService";
import utils from "@/utils/utils";
import text2videoService from '@/api/service/text2videoService'
import utils from '@/utils/utils'
import {
ElMessage, genFileId,
ElMessage,
genFileId,
type UploadInstance,
type UploadProps,
type UploadRawFile
} from "element-plus";
import { nextTick, onMounted, reactive, ref } from "vue";
} from 'element-plus'
import { nextTick, onMounted, reactive, ref } from 'vue'
const debug = ref(import.meta.env.MODE === 'production' ? false : true);
const debug = ref(import.meta.env.MODE === 'production' ? false : true)
// const debug = ref(false);
const loading = ref(false);
const loading = ref(false)
const form = reactive({
task_id: "",
birth: "1976-01",
sex_value: "男",
task_id: '',
birth: '1976-01',
sex_value: '男',
sex_option: [
{
value: '男',
label: '男',
label: '男'
},
{
value: '女50岁退休',
label: '女(普通)50岁退',
label: '女(普通)50岁退'
},
{
value: '女55岁退休',
label: '女(管理、灵活就业)55岁退',
},
label: '女(管理、灵活就业)55岁退'
}
],
retire_age: "",
retire_date: "",
how_long_to_retire: "",
});
retire_age: '',
retire_date: '',
how_long_to_retire: ''
})
onMounted(() => {
// 初始化task_id
form.task_id = utils.genDateTimeStr();
console.log('页面加载,task_id=', form.task_id);
document.title = "延迟退休年龄计算器";
});
form.task_id = utils.genDateTimeStr()
console.log('页面加载,task_id=', form.task_id)
document.title = '延迟退休年龄计算器'
})
const calculateRetirement = (birthStr: string, gender: string) => {
console.log(birthStr)
let parts = birthStr.split('-'); // 使用'-'作为分隔符来拆分字符串
let birthYear = parseInt(parts[0], 10); // 年份
let birthMonth = parseInt(parts[1], 10); // 月份
const currentYear = new Date().getFullYear();
const birthDate = new Date(`${birthYear}-${birthMonth < 10 ? '0' + birthMonth : birthMonth}-01`);
let retirementAge, delayMonths, retirementYear, retirementMonth;
if (gender === '男') {
retirementAge = 60;
delayMonths = Math.ceil(((birthYear - 1965)*12 + birthMonth) / 4);
} else if (gender === '女50岁退休') {
retirementAge = 50;
delayMonths = Math.ceil(((birthYear - 1975)*12 + birthMonth) / 2);
} else if (gender === '女55岁退休') {
retirementAge = 55;
delayMonths = Math.ceil(((birthYear - 1970)*12 + birthMonth) / 4);
} else {
console.log("Invalid gender input. Please choose '男', '女50岁退休', or '女55岁退休'.");
return;
}
if (birthYear >= 1977 && gender == '男') {
delayMonths = 36;
} else if (birthYear >= 1985 && gender == '女50岁退休') {
delayMonths = 60;
} else if (birthYear >= 1982 && gender == '女55岁退休') {
delayMonths = 36;
}
retirementAge += Math.floor(delayMonths / 12);
console.log(retirementAge, delayMonths)
let retirementDate = new Date(birthDate);
console.log(retirementDate)
retirementDate.setFullYear(retirementDate.getFullYear() + retirementAge);
retirementDate.setMonth(retirementDate.getMonth() + delayMonths % 12);
console.log(retirementDate)
retirementYear = retirementDate.getFullYear();
retirementMonth = retirementDate.getMonth() + 1; // getMonth() returns 0-11
console.log(retirementYear, retirementMonth)
let yearsUntilRetirement = retirementDate.getFullYear() - currentYear -1;
let monthsUntilRetirement = (retirementDate.getMonth() - new Date().getMonth() + 12) % 12 -1;
if (monthsUntilRetirement < 0) {
yearsUntilRetirement--;
monthsUntilRetirement += 12;
}
if ((birthYear < 1965 && gender == '男') ||
(birthYear < 1975 && gender == '女50岁退休') ||
(birthYear < 1970 && gender == '女55岁退休')) {
form.retire_age = `您已退休`;
form.retire_date = `您已退休`;
form.how_long_to_retire = `您已退休`;
} else {
form.retire_age = `${retirementAge}岁+${delayMonths % 12}个月`;
form.retire_date = `${retirementYear}-${retirementMonth < 10 ? '0' + retirementMonth : retirementMonth}-01`;
form.how_long_to_retire = `${yearsUntilRetirement}${monthsUntilRetirement}个月`;
}
};
console.log(birthStr)
let parts = birthStr.split('-') // 使用'-'作为分隔符来拆分字符串
let birthYear = parseInt(parts[0], 10) // 年份
let birthMonth = parseInt(parts[1], 10) // 月份
const currentYear = new Date().getFullYear()
const birthDate = new Date(`${birthYear}-${birthMonth < 10 ? '0' + birthMonth : birthMonth}-01`)
let retirementAge, delayMonths, retirementYear, retirementMonth
if (gender === '男') {
retirementAge = 60
delayMonths = Math.ceil(((birthYear - 1965) * 12 + birthMonth) / 4)
} else if (gender === '女50岁退休') {
retirementAge = 50
delayMonths = Math.ceil(((birthYear - 1975) * 12 + birthMonth) / 2)
} else if (gender === '女55岁退休') {
retirementAge = 55
delayMonths = Math.ceil(((birthYear - 1970) * 12 + birthMonth) / 4)
} else {
console.log("Invalid gender input. Please choose '男', '女50岁退休', or '女55岁退休'.")
return
}
if (birthYear >= 1977 && gender == '男') {
delayMonths = 36
} else if (birthYear >= 1985 && gender == '女50岁退休') {
delayMonths = 60
} else if (birthYear >= 1982 && gender == '女55岁退休') {
delayMonths = 36
}
retirementAge += Math.floor(delayMonths / 12)
console.log(retirementAge, delayMonths)
let retirementDate = new Date(birthDate)
console.log(retirementDate)
retirementDate.setFullYear(retirementDate.getFullYear() + retirementAge)
retirementDate.setMonth(retirementDate.getMonth() + (delayMonths % 12))
console.log(retirementDate)
retirementYear = retirementDate.getFullYear()
retirementMonth = retirementDate.getMonth() + 1 // getMonth() returns 0-11
console.log(retirementYear, retirementMonth)
let yearsUntilRetirement = retirementDate.getFullYear() - currentYear - 1
let monthsUntilRetirement = ((retirementDate.getMonth() - new Date().getMonth() + 12) % 12) - 1
if (monthsUntilRetirement < 0) {
yearsUntilRetirement--
monthsUntilRetirement += 12
}
if (
(birthYear < 1965 && gender == '男') ||
(birthYear < 1975 && gender == '女50岁退休') ||
(birthYear < 1970 && gender == '女55岁退休')
) {
form.retire_age = `您已退休`
form.retire_date = `您已退休`
form.how_long_to_retire = `您已退休`
} else {
form.retire_age = `${retirementAge}岁+${delayMonths % 12}个月`
form.retire_date = `${retirementYear}-${
retirementMonth < 10 ? '0' + retirementMonth : retirementMonth
}-01`
form.how_long_to_retire = `${yearsUntilRetirement}${monthsUntilRetirement}个月`
}
}
</script>
<!-- ============================================================================================================ -->
......@@ -133,12 +135,7 @@ const calculateRetirement = (birthStr: string, gender: string) => {
</el-form-item>
<!-- 性别 -->
<el-form-item label="性别">
<el-select
v-model="form.sex_value"
placeholder="Select"
size="large"
style="width: 240px"
>
<el-select v-model="form.sex_value" placeholder="Select" size="large" style="width: 240px">
<el-option
v-for="item in form.sex_option"
:key="item.value"
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<script setup lang="ts">
import text2videoService from "@/api/service/text2videoService";
import utils from "@/utils/utils";
import text2videoService from '@/api/service/text2videoService'
import utils from '@/utils/utils'
import {
ElMessage, genFileId,
ElMessage,
genFileId,
type UploadInstance,
type UploadProps,
type UploadRawFile
} from "element-plus";
import { nextTick, onMounted, reactive, ref } from "vue";
} from 'element-plus'
import { nextTick, onMounted, reactive, ref } from 'vue'
const debug = ref(import.meta.env.MODE === 'production' ? false : true);
const debug = ref(import.meta.env.MODE === 'production' ? false : true)
// const debug = ref(false);
const loading = ref(false);
const loading = ref(false)
const form = reactive({
task_id: "",
task_content: "开发一款坦克大战的小游戏",
task_id: '',
task_content: '开发一款坦克大战的小游戏',
prompt: `你是一名高级的程序员。现在需要根据产品需求,设计一份完整的程序编写的思路,比如第一步写什么模块,第二步写什么模块,整个思路应逻辑清晰、流畅。以json格式输出:\n[{"序号": 序号, "描述": 描述, "详细说明": 详细说明}, ...]\n`,
answer: "",
answer_json: [],
});
answer: '',
answer_json: []
})
const llms = {
tyqw_online: {'api': 'tyqw', 'name':'线上通义千问'},
baichuan: {'api': 'langchain', 'name':'本地baichuan2-7b'},
qwen_local: {'api': 'langchain', 'name':'本地Qwen-7B-Chat'},
chatgpt: {'api': 'gpt', 'name':'chatgpt'},
kimi: {'api': 'kimi', 'name':'kimi'},
};
tyqw_online: { api: 'tyqw', name: '线上通义千问' },
baichuan: { api: 'langchain', name: '本地baichuan2-7b' },
qwen_local: { api: 'langchain', name: '本地Qwen-7B-Chat' },
chatgpt: { api: 'gpt', name: 'chatgpt' },
kimi: { api: 'kimi', name: 'kimi' }
}
onMounted(() => {
// 初始化task_id
form.task_id = utils.genDateTimeStr();
form.task_id = utils.genDateTimeStr()
console.log('页面加载,task_id=', form.task_id)
});
})
const onSubmitGpt = () => {
const my_prompt = ref(`${form.prompt}。产品需求如下:${form.task_content}`);
const my_prompt = ref(`${form.prompt}。产品需求如下:${form.task_content}`)
text2videoService
.submitLLM(utils.aesEncrypt(my_prompt.value), utils.aesEncrypt(llms.tyqw_online.api), [], form.task_id, "true")
.submitLLM(
utils.aesEncrypt(my_prompt.value),
utils.aesEncrypt(llms.tyqw_online.api),
[],
form.task_id,
'true'
)
.then((result: string) => {
console.log(result);
form.answer = result;
const jsonText = utils.extractJSON(result);
console.log(jsonText);
form.answer_json = utils.formatJsonObj(jsonText!);
console.log(result)
form.answer = result
const jsonText = utils.extractJSON(result)
console.log(jsonText)
form.answer_json = utils.formatJsonObj(jsonText!)
})
.catch((error: any) => {
console.error(error);
console.error(error)
ElMessage({
message: error,
type: "error",
});
});
};
type: 'error'
})
})
}
</script>
<!-- ============================================================================================================ -->
......@@ -99,9 +106,7 @@ const onSubmitGpt = () => {
<el-table-column width="200" label="操作" align="center">
<template v-slot="scope">
<div style="margin: 5px 0">
<el-button type="primary" size="small" @click=""
>与大模型继续沟通</el-button
>
<el-button type="primary" size="small" @click="">与大模型继续沟通</el-button>
</div>
<!-- <div style="margin: 10px 0">
......
/// <reference path="./types/index.d.ts" />
declare module 'element-plus/dist/locale/zh-cn.mjs'
declare module 'vue-drag-resize';
\ No newline at end of file
......@@ -68,4 +68,19 @@ declare namespace Wm {
"x": int,
"y": int,
}
interface LoginParams {
username: string
password: string
captcha?: string
}
interface RegisterParams {
confirm_password: string
email?: string
group_ids: number[]
nickname: string
password: string
username: string
}
}
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