commit
fdec69f7ca
38 changed files with 1340 additions and 0 deletions
@ -0,0 +1,6 @@
|
||||
# 局长信箱 群众端(PC) |
||||
## 技术栈 |
||||
- Vue 3 + Vite + (Element Plus)[https://element-plus.gitee.io/zh-CN/component/form.html] |
||||
- sass |
||||
- pinia |
||||
|
||||
@ -0,0 +1,17 @@
|
||||
<!doctype html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<link rel="icon" type="image/*" href="/favicon.png" /> |
||||
<meta name="apple-mobile-web-app-capable" content="yes"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
||||
<title>局长信箱 即接即办</title> |
||||
</head> |
||||
<body> |
||||
<div id="app"></div> |
||||
<script type="module" src="/src/main.js"></script> |
||||
<!-- 科大讯飞语音 --> |
||||
<script src="/lib/index.umd.js"></script> |
||||
<script src="/lib/processor.worker.js"></script> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,26 @@
|
||||
{ |
||||
"name": "mailbox-outer-pc-vue", |
||||
"private": true, |
||||
"version": "0.0.0", |
||||
"type": "module", |
||||
"scripts": { |
||||
"dev": "vite", |
||||
"build": "vite build", |
||||
"preview": "vite preview" |
||||
}, |
||||
"dependencies": { |
||||
"crypto-js": "^4.2.0", |
||||
"pinia": "^2.1.7", |
||||
"element-plus": "^2.2.9", |
||||
"vue": "^3.3.11", |
||||
"vue-router": "^4.2.5" |
||||
}, |
||||
"devDependencies": { |
||||
"@vitejs/plugin-vue": "^4.5.2", |
||||
"sass": "^1.69.7", |
||||
"unplugin-auto-import": "^0.17.3", |
||||
"unplugin-vue-components": "^0.26.0", |
||||
"vite": "^5.0.8", |
||||
"vite-svg-loader": "^5.1.0" |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 755 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).RecorderManager=t()}(this,(function(){"use strict";function e(e,t,r,o){return new(r||(r=Promise))((function(n,a){function i(e){try{u(o.next(e))}catch(e){a(e)}}function s(e){try{u(o.throw(e))}catch(e){a(e)}}function u(e){var t;e.done?n(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(i,s)}u((o=o.apply(e,t||[])).next())}))}function t(e,t){var r,o,n,a,i={label:0,sent:function(){if(1&n[0])throw n[1];return n[1]},trys:[],ops:[]};return a={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function s(s){return function(u){return function(s){if(r)throw new TypeError("Generator is already executing.");for(;a&&(a=0,s[0]&&(i=0)),i;)try{if(r=1,o&&(n=2&s[0]?o.return:s[0]?o.throw||((n=o.return)&&n.call(o),0):o.next)&&!(n=n.call(o,s[1])).done)return n;switch(o=0,n&&(s=[2&s[0],n.value]),s[0]){case 0:case 1:n=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,o=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!(n=i.trys,(n=n.length>0&&n[n.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!n||s[1]>n[0]&&s[1]<n[3])){i.label=s[1];break}if(6===s[0]&&i.label<n[1]){i.label=n[1],n=s;break}if(n&&i.label<n[2]){i.label=n[2],i.ops.push(s);break}n[2]&&i.ops.pop(),i.trys.pop();continue}s=t.call(e,i)}catch(e){s=[6,e],o=0}finally{r=n=0}if(5&s[0])throw s[1];return{value:s[0]?s[1]:void 0,done:!0}}([s,u])}}}function r(){var e,t=navigator,r=t.getUserMedia||t.webkitGetUserMedia||t.mozGetUserMedia;return(null===(e=t.mediaDevices)||void 0===e?void 0:e.getUserMedia)?t.mediaDevices.getUserMedia({audio:!0,video:!1}):r?new Promise((function(e,t){r.call(navigator,{audio:!0,video:!1},(function(t){e(t)}),(function(e){t(e)}))})):Promise.reject(new Error("不支持录音"))}var o;function n(r,n){return e(this,void 0,void 0,(function(){var e;return t(this,(function(t){switch(t.label){case 0:return[3,2];case 1:return t.sent(),[2,new AudioWorkletNode(r,"processor-worklet")];case 2:return(e=o)?[3,4]:[4,new Worker("".concat(n,"/processor.worker.js"))];case 3:e=t.sent(),t.label=4;case 4:return[2,{port:o=e}]}}))}))}return function(){function o(e){this.processorPath=e,this.audioBuffers=[]}return o.prototype.start=function(o){var a,i=o.sampleRate,s=o.frameSize,u=o.arrayBufferType;return e(this,void 0,void 0,(function(){var e,o,c,l,f,d,p;return t(this,(function(t){switch(t.label){case 0:return t.trys.push([0,3,,4]),(e=this).audioBuffers=[],[4,r()];case 1:return o=t.sent(),this.audioTracks=o.getAudioTracks(),c=function(e,t){var r;try{(r=new(window.AudioContext||window.webkitAudioContext)({sampleRate:t})).createMediaStreamSource(e)}catch(t){null==r||r.close(),(r=new(window.AudioContext||window.webkitAudioContext)).createMediaStreamSource(e)}return r}(o,i),this.audioContext=c,l=c.createMediaStreamSource(o),[4,n(c,this.processorPath)];case 2:return f=t.sent(),this.audioWorklet=f,f.port.postMessage({type:"init",data:{frameSize:s,toSampleRate:i||c.sampleRate,fromSampleRate:c.sampleRate,arrayBufferType:u||"short16"}}),f.port.onmessage=function(t){var r=t.data,o=r.frameBuffer,n=r.isLastFrame;if(s&&e.onFrameRecorded)if(null==o?void 0:o.byteLength)for(var a=0;a<o.byteLength;)e.onFrameRecorded({isLastFrame:n&&a+s>=o.byteLength,frameBuffer:t.data.frameBuffer.slice(a,a+s)}),a+=s;else e.onFrameRecorded(t.data);e.onStop&&(o&&e.audioBuffers.push(o),n&&e.onStop(e.audioBuffers))},(d=c.createScriptProcessor(0,1,1)).onaudioprocess=function(e){f.port.postMessage({type:"message",data:e.inputBuffer.getChannelData(0)})},l.connect(d),d.connect(c.destination),c.resume(),null===(a=this.onStart)||void 0===a||a.call(this),[3,4];case 3:return p=t.sent(),console.error(p),[3,4];case 4:return[2]}}))}))},o.prototype.stop=function(){var e,t,r,o;null===(e=this.audioWorklet)||void 0===e||e.port.postMessage({type:"stop"}),null===(t=this.audioTracks)||void 0===t||t[0].stop(),"running"===(null===(r=this.audioContext)||void 0===r?void 0:r.state)&&(null===(o=this.audioContext)||void 0===o||o.close())},o}()})); |
||||
@ -0,0 +1 @@
|
||||
!function(){"use strict";function t(t){return function(t){if(Array.isArray(t))return e(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,r){if(!t)return;if("string"==typeof t)return e(t,r);var i=Object.prototype.toString.call(t).slice(8,-1);"Object"===i&&t.constructor&&(i=t.constructor.name);if("Map"===i||"Set"===i)return Array.from(t);if("Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i))return e(t,r)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function e(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,i=new Array(e);r<e;r++)i[r]=t[r];return i}function r(t,e,r,i){this.fromSampleRate=t,this.toSampleRate=e,this.channels=0|r,this.noReturn=!!i,this.initialize()}r.prototype.initialize=function(){if(!(this.fromSampleRate>0&&this.toSampleRate>0&&this.channels>0))throw new Error("Invalid settings specified for the resampler.");this.fromSampleRate==this.toSampleRate?(this.resampler=this.bypassResampler,this.ratioWeight=1):(this.fromSampleRate<this.toSampleRate?(this.lastWeight=1,this.resampler=this.compileLinearInterpolation):(this.tailExists=!1,this.lastWeight=0,this.resampler=this.compileMultiTap),this.ratioWeight=this.fromSampleRate/this.toSampleRate)},r.prototype.compileLinearInterpolation=function(t){var e=t.length;this.initializeBuffers(e);var r,i,s=this.outputBufferSize,a=this.ratioWeight,f=this.lastWeight,n=0,o=0,h=0,l=this.outputBuffer;if(e%this.channels==0){if(e>0){for(;f<1;f+=a)for(n=1-(o=f%1),r=0;r<this.channels;++r)l[h++]=this.lastOutput[r]*n+t[r]*o;for(f--,e-=this.channels,i=Math.floor(f)*this.channels;h<s&&i<e;){for(n=1-(o=f%1),r=0;r<this.channels;++r)l[h++]=t[i+r]*n+t[i+this.channels+r]*o;f+=a,i=Math.floor(f)*this.channels}for(r=0;r<this.channels;++r)this.lastOutput[r]=t[i++];return this.lastWeight=f%1,this.bufferSlice(h)}return this.noReturn?0:[]}throw new Error("Buffer was of incorrect sample length.")},r.prototype.compileMultiTap=function(t){var e=[],r=t.length;this.initializeBuffers(r);var i=this.outputBufferSize;if(r%this.channels==0){if(r>0){for(var s=this.ratioWeight,a=0,f=0;f<this.channels;++f)e[f]=0;var n=0,o=0,h=!this.tailExists;this.tailExists=!1;var l=this.outputBuffer,u=0,p=0;do{if(h)for(a=s,f=0;f<this.channels;++f)e[f]=0;else{for(a=this.lastWeight,f=0;f<this.channels;++f)e[f]+=this.lastOutput[f];h=!0}for(;a>0&&n<r;){if(!(a>=(o=1+n-p))){for(f=0;f<this.channels;++f)e[f]+=t[n+f]*a;p+=a,a=0;break}for(f=0;f<this.channels;++f)e[f]+=t[n++]*o;p=n,a-=o}if(0!=a){for(this.lastWeight=a,f=0;f<this.channels;++f)this.lastOutput[f]=e[f];this.tailExists=!0;break}for(f=0;f<this.channels;++f)l[u++]=e[f]/s}while(n<r&&u<i);return this.bufferSlice(u)}return this.noReturn?0:[]}throw new Error("Buffer was of incorrect sample length.")},r.prototype.bypassResampler=function(t){return this.noReturn?(this.outputBuffer=t,t.length):t},r.prototype.bufferSlice=function(t){if(this.noReturn)return t;try{return this.outputBuffer.subarray(0,t)}catch(e){try{return this.outputBuffer.length=t,this.outputBuffer}catch(e){return this.outputBuffer.slice(0,t)}}},r.prototype.initializeBuffers=function(t){this.outputBufferSize=Math.ceil(t*this.toSampleRate/this.fromSampleRate);try{this.outputBuffer=new Float32Array(this.outputBufferSize),this.lastOutput=new Float32Array(this.channels)}catch(t){this.outputBuffer=[],this.lastOutput=[]}},self.transData=function(t){return"short16"===self.arrayBufferType&&(t=function(t){for(var e=new ArrayBuffer(2*t.length),r=new DataView(e),i=0,s=0;s<t.length;s+=1,i+=2){var a=Math.max(-1,Math.min(1,t[s]));r.setInt16(i,a<0?32768*a:32767*a,!0)}return r.buffer}(t=self.resampler.resampler(t))),t},self.onmessage=function(e){var i=e.data,s=i.type,a=i.data;if("init"===s){var f=a.frameSize,n=a.toSampleRate,o=a.fromSampleRate,h=a.arrayBufferType;return self.frameSize=f*Math.floor(o/n),self.resampler=new r(o,n,1),self.frameBuffer=[],void(self.arrayBufferType=h)}if("stop"===s&&(self.postMessage({frameBuffer:self.transData(self.frameBuffer),isLastFrame:!0}),self.frameBuffer=[]),"message"===s){var l,u=a;if(self.frameSize)return(l=self.frameBuffer).push.apply(l,t(u)),self.frameBuffer.length>=self.frameSize&&(self.postMessage({frameBuffer:self.transData(this.frameBuffer),isLastFrame:!1}),self.frameBuffer=[]),!0;u&&self.postMessage({frameBuffer:self.transData(u),isLastFrame:!1})}}}(); |
||||
|
After Width: | Height: | Size: 202 KiB |
@ -0,0 +1,12 @@
|
||||
import { get, post } from '@/util/request' |
||||
|
||||
|
||||
// 授权登录
|
||||
export function authOpenid(openid) { |
||||
return post('/auth/openid?openid=' + openid) |
||||
} |
||||
|
||||
// 获取用户信息
|
||||
export function userInfo() { |
||||
return get('/auth/user') |
||||
} |
||||
@ -0,0 +1,21 @@
|
||||
import { get, post, put } from '@/util/request' |
||||
|
||||
// 新增信件
|
||||
export function addMail(data) { |
||||
return post('/mail', data) |
||||
} |
||||
|
||||
// 信件列表 分页
|
||||
export function listMail(params) { |
||||
return get(`/mail?size=${params.size}¤t=${params.current}`) |
||||
} |
||||
|
||||
// 获取信件详情
|
||||
export function getMail(id) { |
||||
return get('/mail/' + id) |
||||
} |
||||
|
||||
// 信件评论
|
||||
export function updateEvaluate(data) { |
||||
return put('/mail/evaluate', data) |
||||
} |
||||
@ -0,0 +1,21 @@
|
||||
import { get, post, del } from '@/util/request' |
||||
|
||||
// 保存草稿
|
||||
export function saveDraft(data) { |
||||
return post('/mail/draft', data) |
||||
} |
||||
|
||||
// 获取草稿详情
|
||||
export function getDraft(id) { |
||||
return get('/mail/draft/' + id) |
||||
} |
||||
|
||||
// 信件草稿列表 分页
|
||||
export function listDraft(params) { |
||||
return get(`/mail/draft?size=${params.size}¤t=${params.current}`) |
||||
} |
||||
|
||||
// 删除信件
|
||||
export function delDraft(id) { |
||||
return del('/mail/draft/' + id) |
||||
} |
||||
@ -0,0 +1,5 @@
|
||||
import { post } from '@/util/request' |
||||
|
||||
export function send(phone) { |
||||
return post('/sms/send?phone=' + phone) |
||||
} |
||||
@ -0,0 +1,212 @@
|
||||
body { |
||||
font-size: 14px; |
||||
font-family: PingFang-SC-Heavy; |
||||
margin: 0; |
||||
--header-height: 8.377vh; |
||||
background-color: var(--background-color); |
||||
} |
||||
|
||||
#app { |
||||
max-width: 1200px; |
||||
margin: auto; |
||||
} |
||||
|
||||
p { |
||||
margin: 0.5em 0; |
||||
} |
||||
|
||||
img { |
||||
max-width: 100%; |
||||
} |
||||
|
||||
svg { |
||||
width: 1em; |
||||
} |
||||
|
||||
svg+span { |
||||
margin-left: .5em; |
||||
} |
||||
|
||||
.none { |
||||
display: none; |
||||
} |
||||
|
||||
.flex { |
||||
display: flex; |
||||
} |
||||
|
||||
.flex-inline { |
||||
display: inline-flex; |
||||
} |
||||
|
||||
.flex.v-center, |
||||
.flex-inline.v-center { |
||||
align-items: center; |
||||
} |
||||
|
||||
.flex.center, |
||||
.flex-inline.center { |
||||
justify-content: center; |
||||
} |
||||
|
||||
.flex.between, |
||||
.flex-inline.between { |
||||
justify-content: space-between; |
||||
} |
||||
|
||||
.flex.end, |
||||
.flex-inline.end { |
||||
justify-content: flex-end; |
||||
} |
||||
|
||||
.flex.wrap, |
||||
.flex-inline.wrap { |
||||
flex-wrap: wrap; |
||||
} |
||||
|
||||
.flex.max-content, |
||||
.flex-inline.max-content { |
||||
width: max-content; |
||||
} |
||||
|
||||
.flex.column, .flex-inline.column { |
||||
flex-direction: column; |
||||
} |
||||
|
||||
.flex.gap, |
||||
.flex-inline.gap { |
||||
gap: 0 8px; |
||||
} |
||||
|
||||
.flex.gap-10 { |
||||
gap: 0 10px; |
||||
} |
||||
|
||||
.flex.gap-16 { |
||||
gap: 0 16px; |
||||
} |
||||
|
||||
.text-center { |
||||
text-align: center; |
||||
} |
||||
|
||||
.text-nowrap { |
||||
white-space: nowrap; |
||||
text-overflow: ellipsis; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.text-wrap { |
||||
white-space: pre-wrap; |
||||
} |
||||
|
||||
.container { |
||||
padding: 18px 36px; |
||||
} |
||||
|
||||
.pointer:hover { |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.relative { |
||||
position: relative; |
||||
} |
||||
|
||||
.ml-4 { |
||||
margin-left: 4px; |
||||
} |
||||
|
||||
.ml-8 { |
||||
margin-left: 8px; |
||||
} |
||||
|
||||
.ml-10 { |
||||
margin-left: 10px; |
||||
} |
||||
|
||||
.mr-4 { |
||||
margin-right: 4px; |
||||
} |
||||
|
||||
.mr-8 { |
||||
margin-right: 8px; |
||||
} |
||||
|
||||
.mr-10 { |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.mr-20 { |
||||
margin-right: 20px; |
||||
} |
||||
|
||||
.mt-8 { |
||||
margin-top: 8px; |
||||
} |
||||
|
||||
.mt-10 { |
||||
margin-top: 10px; |
||||
} |
||||
|
||||
.mt-20 { |
||||
margin-top: 20px; |
||||
} |
||||
|
||||
.mb-8 { |
||||
margin-bottom: 8px; |
||||
} |
||||
|
||||
.mb-10 { |
||||
margin-bottom: 10px; |
||||
} |
||||
|
||||
.mb-20 { |
||||
margin-bottom: 20px; |
||||
} |
||||
|
||||
.mb-40 { |
||||
margin-bottom: 40px; |
||||
} |
||||
|
||||
|
||||
.card { |
||||
background-color: #fff; |
||||
margin-bottom: 10px; |
||||
padding-top: 6px; |
||||
|
||||
h2 { |
||||
color: #666; |
||||
font-size: 12px; |
||||
font-weight: normal; |
||||
margin: var(--van-cell-group-inset-padding); |
||||
padding: var(--van-cell-vertical-padding) 0; |
||||
} |
||||
|
||||
header { |
||||
margin: var(--van-cell-group-inset-padding); |
||||
margin-top: 16px; |
||||
font-size: 17px; |
||||
font-weight: bold; |
||||
} |
||||
|
||||
.content { |
||||
box-shadow: inset 0px -1px 0px 0px rgba(227, 227, 227, 1); |
||||
color: #666; |
||||
margin-top: 10px; |
||||
padding: var(--van-cell-group-inset-padding); |
||||
padding-bottom: 30px; |
||||
font-size: 12px; |
||||
font-weight: 400; |
||||
} |
||||
|
||||
footer { |
||||
box-shadow: inset 0px -1px 0px 0px rgba(227, 227, 227, 1); |
||||
padding: var(--van-cell-group-inset-padding); |
||||
} |
||||
} |
||||
|
||||
.wrapper { |
||||
height: 100vh; |
||||
background-color: var(--background-color); |
||||
overflow: auto; |
||||
} |
||||
@ -0,0 +1,8 @@
|
||||
// @/styles/element/index.scss |
||||
@forward "element-plus/theme-chalk/src/common/var.scss" with ( |
||||
$colors: ( |
||||
"primary": ( |
||||
"base": #162582, |
||||
) |
||||
) |
||||
); |
||||
@ -0,0 +1,29 @@
|
||||
<template> |
||||
<div :style="{ fontSize: size ? `${size}px` : '' }"> |
||||
<IconSvg /> |
||||
</div> |
||||
</template> |
||||
<script setup> |
||||
const props = defineProps({ |
||||
name: { |
||||
type: String, |
||||
require: true, |
||||
}, |
||||
size: { |
||||
type: Number, |
||||
}, |
||||
}); |
||||
|
||||
let IconSvg = ref(h("template")); |
||||
|
||||
import(`@/assets/icons/${props.name}.svg`).then((data) => { |
||||
IconSvg.value = data.render(); |
||||
}); |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
:deep() { |
||||
svg { |
||||
display: block; |
||||
} |
||||
} |
||||
</style> |
||||
@ -0,0 +1,44 @@
|
||||
<template> |
||||
<div v-if="show" @click="hide" class="img-preview-container flex v-center center"> |
||||
<img :src="filepath" alt="" /> |
||||
</div> |
||||
</template> |
||||
<script setup> |
||||
import { watch } from "vue" |
||||
|
||||
const props = defineProps({ |
||||
filepath: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
show: { |
||||
type: Boolean, |
||||
default: false |
||||
} |
||||
}) |
||||
const emit = defineEmits(['update:show']) |
||||
|
||||
const show = ref(false) |
||||
|
||||
watch(() => props.show, (val) => { |
||||
show.value = val |
||||
}) |
||||
|
||||
function hide() { |
||||
emit('update:show', false) |
||||
} |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
.img-preview-container { |
||||
position: fixed; |
||||
top: 0; |
||||
left: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
background-color: #000; |
||||
z-index: 2; |
||||
img { |
||||
max-width: 100%; |
||||
} |
||||
} |
||||
</style> |
||||
@ -0,0 +1,74 @@
|
||||
<template> |
||||
<div class="loading-wrapper flex v-center center" v-if="loading"> |
||||
<div class="loading flex center v-center column"> |
||||
<div class="loader-box" v-if="icon === 'loading'"> |
||||
<div class="loader"></div> |
||||
</div> |
||||
<div v-else-if="icon === 'img'" class="mb-10"> |
||||
<Icon name="img" :size="84" /> |
||||
</div> |
||||
<div>{{ message }}</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
<script setup> |
||||
|
||||
defineProps({ |
||||
loading: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
message: { |
||||
type: String, |
||||
default: '加载中...' |
||||
}, |
||||
icon : { |
||||
type: String, |
||||
default: 'loading' |
||||
} |
||||
}) |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
.loading-wrapper { |
||||
position: fixed; |
||||
top: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
left: 0; |
||||
z-index: 999; |
||||
.loading { |
||||
background: rgba(0,0,0,0.6); |
||||
width: 61vw; |
||||
max-width: 600px; |
||||
height: 30vh; |
||||
border-radius: 6px; |
||||
color: #fff; |
||||
.loader-box { |
||||
width: 32px; |
||||
height: 32px; |
||||
transform: translate(50%, 50%); |
||||
margin-bottom: 28px; |
||||
} |
||||
} |
||||
} |
||||
/* HTML: <div class="loader"></div> */ |
||||
.loader { |
||||
--d:22px; |
||||
width: 4px; |
||||
height: 4px; |
||||
border-radius: 50%; |
||||
color: #fff; |
||||
box-shadow: |
||||
calc(1*var(--d)) calc(0*var(--d)) 0 0, |
||||
calc(0.707*var(--d)) calc(0.707*var(--d)) 0 1px, |
||||
calc(0*var(--d)) calc(1*var(--d)) 0 2px, |
||||
calc(-0.707*var(--d)) calc(0.707*var(--d)) 0 3px, |
||||
calc(-1*var(--d)) calc(0*var(--d)) 0 4px, |
||||
calc(-0.707*var(--d)) calc(-0.707*var(--d))0 5px, |
||||
calc(0*var(--d)) calc(-1*var(--d)) 0 6px; |
||||
animation: l27 1s infinite steps(8); |
||||
} |
||||
@keyframes l27 { |
||||
100% {transform: rotate(1turn)} |
||||
} |
||||
</style> |
||||
@ -0,0 +1,14 @@
|
||||
<template> |
||||
<div class="wrapper"> |
||||
<header> |
||||
<img src="/imgs/bg_web.png" alt="" /> |
||||
</header> |
||||
<main> |
||||
<router-view /> |
||||
</main> |
||||
</div> |
||||
</template> |
||||
<script setup> |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
</style> |
||||
@ -0,0 +1,18 @@
|
||||
import { createApp } from 'vue' |
||||
import router from './router/index' |
||||
import { createPinia } from 'pinia' |
||||
|
||||
import App from './App.vue' |
||||
import IconComponent from '@/components/Icon.vue' |
||||
import LoadingComponent from '@/components/Loading.vue' |
||||
|
||||
import './permission' // 权限控制
|
||||
|
||||
import './assets/style/style.scss' |
||||
|
||||
const app = createApp(App) |
||||
.use(router) |
||||
.use(createPinia()) |
||||
.component('Icon', IconComponent) |
||||
.component('Loading', LoadingComponent); |
||||
app.mount('#app') |
||||
@ -0,0 +1,51 @@
|
||||
import router from './router' |
||||
import UserStore from "@/store/user" |
||||
import { getToken } from '@/util/cookie' |
||||
|
||||
// 白名单
|
||||
const whiteList = ['/', '/mail/write']; |
||||
router.beforeEach((to, from, next) => { |
||||
const whiteFlag = whiteList.indexOf(to.path) !== -1; |
||||
const userStore = UserStore(); |
||||
const token = getToken(); |
||||
// 没有token && 并且不是白名单中的路由
|
||||
if (whiteFlag) { |
||||
next(); |
||||
return |
||||
} |
||||
if (!token) { |
||||
next('/'); |
||||
return |
||||
} |
||||
// 已有用户信息
|
||||
if (userStore.user.id) { |
||||
// 判断用户是否进行了人脸认证
|
||||
if (userStore.user.idCard || to.path === '/realName/auth') { |
||||
// 判断是否是从信件页面回退到人脸认证界面的,如果是则返回首页
|
||||
if (to.path === '/realName/auth' && from.path === '/mail') { |
||||
next('/'); |
||||
return |
||||
} |
||||
next(); |
||||
return |
||||
}
|
||||
next('/realName/auth'); |
||||
return |
||||
} |
||||
// 获取用户信息
|
||||
userStore.getUser().then(() => { |
||||
// 判断用户是否进行了人脸认证
|
||||
if (userStore.user.idCard || to.path === '/realName/auth') { |
||||
// 判断是否是从信件页面回退到人脸认证界面的,如果是则返回首页
|
||||
if (to.path === '/realName/auth' && from.path === '/mail') { |
||||
next('/'); |
||||
return |
||||
} |
||||
next(); |
||||
return |
||||
}
|
||||
next('/realName/auth'); |
||||
}).catch((res) => { |
||||
next('/') |
||||
}) |
||||
}) |
||||
@ -0,0 +1,39 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router' |
||||
|
||||
import Layout from '@/layout/Index.vue' |
||||
import Home from '@/views/Home.vue' |
||||
import Write from '@/views/Write.vue' |
||||
|
||||
import NotFound from '@/views/error/404.vue' |
||||
|
||||
const routes = [ |
||||
{ |
||||
path: '/layout', |
||||
component: Layout, |
||||
children: [ |
||||
{ |
||||
path: '/', |
||||
component: Home, |
||||
}, |
||||
{ |
||||
path: '/mail/write', |
||||
component: Write, |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
path: '/:catchAll(.*)', |
||||
name: 'not-found', |
||||
component: NotFound, |
||||
meta: { |
||||
title: '404' |
||||
} |
||||
} |
||||
]; |
||||
|
||||
const router = createRouter({ |
||||
history: createWebHistory(), |
||||
routes |
||||
}); |
||||
|
||||
export default router; |
||||
@ -0,0 +1,51 @@
|
||||
|
||||
export const depts = [ |
||||
{ |
||||
text: "芙蓉分局", |
||||
value: 6, |
||||
}, |
||||
{ |
||||
text: "天心分局", |
||||
value: 29, |
||||
}, |
||||
{ |
||||
text: "岳麓分局", |
||||
value: 34, |
||||
}, |
||||
{ |
||||
text: "开福分局", |
||||
value: 20, |
||||
}, |
||||
{ |
||||
text: "雨花分局", |
||||
value: 33, |
||||
}, |
||||
{ |
||||
text: "高新分局", |
||||
value: 7, |
||||
}, |
||||
{ |
||||
text: "望城分局", |
||||
value: 31, |
||||
}, |
||||
{ |
||||
text: "长沙县局", |
||||
value: 35, |
||||
}, |
||||
{ |
||||
text: "浏阳市局", |
||||
value: 22, |
||||
}, |
||||
{ |
||||
text: "宁乡市局", |
||||
value: 24, |
||||
}, |
||||
{ |
||||
text: "交警支队", |
||||
value: 14, |
||||
}, |
||||
{ |
||||
text: "其他单位", |
||||
value: 0, |
||||
}, |
||||
] |
||||
@ -0,0 +1,15 @@
|
||||
import { defineStore } from 'pinia' |
||||
|
||||
const PageStore = defineStore( |
||||
'page', |
||||
{ |
||||
state: () => ({ |
||||
mailTabActive: '', |
||||
mailDraftId: '', |
||||
myMailRefresh: false, |
||||
myMailDraftRefresh: false |
||||
}) |
||||
} |
||||
) |
||||
|
||||
export default PageStore |
||||
@ -0,0 +1,28 @@
|
||||
import { defineStore } from 'pinia' |
||||
|
||||
import { userInfo } from "@/api/auth"; |
||||
import { delToken } from '@/util/cookie' |
||||
|
||||
const UserStore = defineStore( |
||||
'user', |
||||
{ |
||||
state: () => ({ |
||||
user: {} |
||||
}), |
||||
actions: { |
||||
getUser() { |
||||
return new Promise((resolve, reject) => { |
||||
userInfo().then((data) => { |
||||
this.user = data; |
||||
resolve(data); |
||||
}).catch(res => { |
||||
delToken() |
||||
reject(res) |
||||
}) |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
) |
||||
|
||||
export default UserStore |
||||
@ -0,0 +1,169 @@
|
||||
import CryptoJS from 'crypto-js'; |
||||
|
||||
export function startRecorder(stopCallback, speechText) { |
||||
console.log("开始录音"); |
||||
const recorder = new RecorderManager("lib"); |
||||
let iatWS; |
||||
connectWebSocket(); |
||||
recorder.onStart = () => { |
||||
// 开始
|
||||
}; |
||||
let resultText = ""; |
||||
let resultTextTemp = ""; |
||||
let result = ""; |
||||
let flag = false; |
||||
recorder.onFrameRecorded = ({ isLastFrame, frameBuffer }) => { |
||||
if (iatWS.readyState === iatWS.OPEN) { |
||||
iatWS.send( |
||||
JSON.stringify({ |
||||
data: { |
||||
status: isLastFrame ? 2 : 1, |
||||
format: "audio/L16;rate=16000", |
||||
encoding: "raw", |
||||
audio: toBase64(frameBuffer), |
||||
}, |
||||
}) |
||||
); |
||||
if (isLastFrame) { |
||||
|
||||
} |
||||
} |
||||
}; |
||||
|
||||
recorder.onStop = () => { |
||||
console.log('onStop') |
||||
flag = true |
||||
}; |
||||
|
||||
function renderResult(resultData) { |
||||
// 识别结束
|
||||
let jsonData = JSON.parse(resultData); |
||||
console.log('jsonData', jsonData) |
||||
if (jsonData.data && jsonData.data.result) { |
||||
let data = jsonData.data.result; |
||||
let str = ""; |
||||
let ws = data.ws; |
||||
for (let i = 0; i < ws.length; i++) { |
||||
str = str + ws[i].cw[0].w; |
||||
console.log('str', str) |
||||
speechText.value = str |
||||
} |
||||
// 开启wpgs会有此字段(前提:在控制台开通动态修正功能)
|
||||
// 取值为 "apd"时表示该片结果是追加到前面的最终结果;取值为"rpl" 时表示替换前面的部分结果,替换范围为rg字段
|
||||
if (data.pgs) { |
||||
if (data.pgs === "apd") { |
||||
// 将resultTextTemp同步给resultText
|
||||
resultText = resultTextTemp; |
||||
console.log('resultText', resultText) |
||||
} |
||||
// 将结果存储在resultTextTemp中
|
||||
resultTextTemp = resultText + str; |
||||
} else { |
||||
resultText = resultText + str; |
||||
} |
||||
|
||||
result += resultTextTemp || resultText || "" |
||||
resultTextTemp = "" |
||||
resultText = ""; |
||||
console.log('result', result) |
||||
speechText.value = result |
||||
if (flag) { |
||||
// 回调
|
||||
stopCallback(result) |
||||
console.log("录音结束"); |
||||
} |
||||
} |
||||
|
||||
// if (jsonData.code === 0 && jsonData.data.status === 2) {
|
||||
// iatWS.close();
|
||||
// }
|
||||
// if (jsonData.code !== 0) {
|
||||
// iatWS.close();
|
||||
// console.error(jsonData);
|
||||
// }
|
||||
} |
||||
|
||||
function connectWebSocket() { |
||||
const websocketUrl = getWebSocketUrl(); |
||||
if ("WebSocket" in window) { |
||||
iatWS = new WebSocket(websocketUrl); |
||||
} else if ("MozWebSocket" in window) { |
||||
iatWS = new MozWebSocket(websocketUrl); |
||||
} else { |
||||
console.error("浏览器不支持WebSocket"); |
||||
return; |
||||
} |
||||
iatWS.onopen = (e) => { |
||||
// 开始录音
|
||||
recorder.start({ |
||||
sampleRate: 16000, |
||||
frameSize: 1280, |
||||
}); |
||||
var params = { |
||||
common: { |
||||
app_id: "3e5c642a", |
||||
}, |
||||
business: { |
||||
language: "zh_cn", |
||||
domain: "iat", |
||||
accent: "mandarin", |
||||
vad_eos: 5000, |
||||
dwa: "wpgs", |
||||
}, |
||||
data: { |
||||
status: 0, |
||||
format: "audio/L16;rate=16000", |
||||
encoding: "raw", |
||||
}, |
||||
}; |
||||
iatWS.send(JSON.stringify(params)); |
||||
}; |
||||
iatWS.onmessage = (e) => { |
||||
console.log('ws onmessage') |
||||
renderResult(e.data); |
||||
}; |
||||
iatWS.onerror = (e) => { |
||||
console.log('ws 错误') |
||||
recorder.stop(); |
||||
}; |
||||
iatWS.onclose = (e) => { |
||||
console.log('ws 关闭') |
||||
|
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* 获取websocket url |
||||
* 该接口需要后端提供,这里为了方便前端处理 |
||||
*/ |
||||
function getWebSocketUrl() { |
||||
// 请求地址根据语种不同变化
|
||||
var url = "wss://iat-api.xfyun.cn/v2/iat"; |
||||
var host = "iat-api.xfyun.cn"; |
||||
var apiKey = "b4d5965f32492c53e74740f5056d9a21"; |
||||
var apiSecret = "Nzc5ZDIzM2QxZGMxYWFhODk0NWIxZjg4"; |
||||
var date = new Date().toGMTString(); |
||||
var algorithm = "hmac-sha256"; |
||||
var headers = "host date request-line"; |
||||
var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`; |
||||
var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret); |
||||
var signature = CryptoJS.enc.Base64.stringify(signatureSha); |
||||
var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`; |
||||
var authorization = btoa(authorizationOrigin); |
||||
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`; |
||||
return url; |
||||
} |
||||
|
||||
function toBase64(buffer) { |
||||
var binary = ""; |
||||
var bytes = new Uint8Array(buffer); |
||||
var len = bytes.byteLength; |
||||
for (var i = 0; i < len; i++) { |
||||
binary += String.fromCharCode(bytes[i]); |
||||
} |
||||
return window.btoa(binary); |
||||
} |
||||
|
||||
return recorder; |
||||
} |
||||
|
||||
@ -0,0 +1,39 @@
|
||||
|
||||
function setCookieByHour(cookieName, cookieValue, expirationDays) { |
||||
var d = new Date(); |
||||
d.setTime(d.getTime() + (expirationDays * 60 * 60 * 1000)); |
||||
var expires = "expires=" + d.toUTCString(); |
||||
document.cookie = cookieName + "=" + cookieValue + ";" + expires + ";path=/"; |
||||
} |
||||
|
||||
export function getCookie(cookieName) { |
||||
var name = cookieName + "="; |
||||
var decodedCookie = decodeURIComponent(document.cookie); |
||||
var cookieArray = decodedCookie.split(';'); |
||||
for (var i = 0; i < cookieArray.length; i++) { |
||||
var cookie = cookieArray[i].trim(); |
||||
if (cookie.indexOf(name) === 0) { |
||||
return cookie.substring(name.length, cookie.length); |
||||
} |
||||
} |
||||
return ""; |
||||
} |
||||
|
||||
function deleteCookie(cookieName) { |
||||
// 设置cookie的过期时间为过去的日期
|
||||
document.cookie = cookieName + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; |
||||
} |
||||
|
||||
const TOKEN_COOKIE_NAME = "token"; |
||||
|
||||
export function setToken(key) { |
||||
setCookieByHour(TOKEN_COOKIE_NAME, key, 1) |
||||
} |
||||
|
||||
export function getToken() { |
||||
return getCookie(TOKEN_COOKIE_NAME) |
||||
} |
||||
|
||||
export function delToken() { |
||||
return deleteCookie(TOKEN_COOKIE_NAME) |
||||
} |
||||
@ -0,0 +1,26 @@
|
||||
|
||||
export function getFileType(originFilename) { |
||||
const suffix = |
||||
originFilename.indexOf(".") > -1 |
||||
? originFilename |
||||
.substring(originFilename.lastIndexOf(".") + 1) |
||||
.toLocaleLowerCase() |
||||
: ""; |
||||
let filetype = ""; |
||||
if ( |
||||
suffix === "jpg" || |
||||
suffix === "png" || |
||||
suffix === "jpeg" || |
||||
suffix === "bmp" || |
||||
suffix === "gif" |
||||
) { |
||||
filetype = "img"; |
||||
} else if (suffix === "mp3" || suffix === "wav") { |
||||
filetype = "mp3"; |
||||
} else if (suffix === "mp4") { |
||||
filetype = "mp4"; |
||||
} else { |
||||
filetype = "file"; |
||||
} |
||||
return filetype; |
||||
} |
||||
@ -0,0 +1,76 @@
|
||||
|
||||
const basePath = '/api' |
||||
import { getToken } from '@/util/cookie' |
||||
|
||||
export function get(url) { |
||||
return request(url, { |
||||
method: 'GET', |
||||
headers: { |
||||
"Content-Type": 'application/json', |
||||
"Authorization": getToken() |
||||
} |
||||
}) |
||||
} |
||||
|
||||
export function post(url, data) { |
||||
return request(url, { |
||||
method: 'POST', |
||||
body: JSON.stringify(data), |
||||
headers: { |
||||
"Content-Type": 'application/json', |
||||
"Authorization": getToken() |
||||
} |
||||
}) |
||||
} |
||||
|
||||
export function put(url, data) { |
||||
return request(url, { |
||||
method: 'PUT', |
||||
body: JSON.stringify(data), |
||||
headers: { |
||||
"Content-Type": 'application/json', |
||||
"Authorization": getToken() |
||||
} |
||||
}) |
||||
} |
||||
|
||||
export function del(url) { |
||||
return request(url, { |
||||
method: 'DELETE', |
||||
headers: { |
||||
"Content-Type": 'application/json', |
||||
"Authorization": getToken() |
||||
} |
||||
}) |
||||
} |
||||
|
||||
export function upload(data) { |
||||
return post('/file/upload/base64', data) |
||||
} |
||||
|
||||
function request(url, options) { |
||||
return new Promise((resolve, reject) => { |
||||
fetch(`${basePath}${url}`, { |
||||
method: options.method, |
||||
body: options.body, |
||||
headers: options.headers |
||||
}).then(response => { |
||||
if (response.status === 413) { |
||||
return; |
||||
} |
||||
return response.json(); |
||||
}).then(res => { |
||||
if (res.code === 200) { |
||||
resolve(res.data) |
||||
} else { |
||||
let message = res.msg; |
||||
if (res.code === 401) { |
||||
message = "未授权登陆" |
||||
} else { |
||||
} |
||||
reject(res) |
||||
} |
||||
|
||||
}) |
||||
}) |
||||
} |
||||
@ -0,0 +1,12 @@
|
||||
|
||||
export function getSex(idCard) { |
||||
let res = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/; |
||||
if (idCard && res.test(idCard)) { |
||||
let genderCode = idCard.charAt(16); |
||||
if (parseInt(genderCode) % 2 == 0) { |
||||
return 'F'; |
||||
} |
||||
return 'M'; |
||||
} |
||||
return ''; |
||||
} |
||||
@ -0,0 +1,93 @@
|
||||
/** |
||||
* 验证身份证号码 |
||||
* @param { String } code 身份证号码 |
||||
*/ |
||||
export function validatorIdCard(code) { |
||||
// 身份证号前两位代表区域
|
||||
const city = { |
||||
11: '北京', |
||||
12: '天津', |
||||
13: '河北', |
||||
14: '山西', |
||||
15: '内蒙古', |
||||
21: '辽宁', |
||||
22: '吉林', |
||||
23: '黑龙江 ', |
||||
31: '上海', |
||||
32: '江苏', |
||||
33: '浙江', |
||||
34: '安徽', |
||||
35: '福建', |
||||
36: '江西', |
||||
37: '山东', |
||||
41: '河南', |
||||
42: '湖北 ', |
||||
43: '湖南', |
||||
44: '广东', |
||||
45: '广西', |
||||
46: '海南', |
||||
50: '重庆', |
||||
51: '四川', |
||||
52: '贵州', |
||||
53: '云南', |
||||
54: '西藏 ', |
||||
61: '陕西', |
||||
62: '甘肃', |
||||
63: '青海', |
||||
64: '宁夏', |
||||
65: '新疆', |
||||
71: '台湾', |
||||
81: '香港', |
||||
82: '澳门', |
||||
91: '国外 ', |
||||
}; |
||||
const idCardReg = /^[1-9]\d{5}(19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i; // 身份证格式正则表达式
|
||||
// 如果身份证不满足格式正则表达式
|
||||
if (!code) { |
||||
return '请输入身份证号码' |
||||
} |
||||
if (!code.match(idCardReg)) { |
||||
return '请输入正确的身份证号码'; |
||||
}
|
||||
if (!city[code.substr(0, 2)]) { |
||||
// 区域数组中不包含需验证的身份证前两位
|
||||
return '请输入正确的身份证号码'; |
||||
} |
||||
if (code.length === 18) { |
||||
// 18位身份证需要验证最后一位校验位
|
||||
code = code.split(''); |
||||
// ∑(ai×Wi)(mod 11)
|
||||
// 加权因子
|
||||
const factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; |
||||
// 校验位
|
||||
const parity = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2]; |
||||
let sum = 0; |
||||
let ai = 0; |
||||
let wi = 0; |
||||
for (let i = 0; i < 17; i++) { |
||||
ai = parseInt(code[i]); |
||||
wi = factor[i]; |
||||
sum += ai * wi; // 开始计算并相加
|
||||
} |
||||
const last = parity[sum % 11]; // 求余
|
||||
if (last.toString() !== code[17]) { |
||||
return '请输入正确的身份证号码'; |
||||
} |
||||
} |
||||
return true |
||||
} |
||||
|
||||
/** |
||||
* 校验手机号 |
||||
* @param {*} phonenumber
|
||||
* @returns
|
||||
*/ |
||||
export function validatorPhone(phonenumber) { |
||||
if (!phonenumber) { |
||||
return '请输入手机号码' |
||||
} |
||||
if (!/^1[3456789]\d{9}/.test(phonenumber)) { |
||||
return '请输入手机号码' |
||||
} |
||||
return true |
||||
} |
||||
@ -0,0 +1,75 @@
|
||||
<template> |
||||
<div class="flex gap-10 center mt-20"> |
||||
<a |
||||
class="flex v-center center gap-16" |
||||
@click="router.push('/mail/write')" |
||||
> |
||||
<div style="width: 48px"> |
||||
<img src="/imgs/write.png" alt="" /> |
||||
</div> |
||||
<span>我要写信</span> |
||||
</a> |
||||
<a |
||||
class="flex v-center center gap-16" |
||||
@click="router.push('/mail?active=my')" |
||||
> |
||||
<div style="width: 48px"> |
||||
<img src="/imgs/search.png" alt="" /> |
||||
</div> |
||||
<span>回复查询</span> |
||||
</a> |
||||
</div> |
||||
|
||||
<Loading :loading="loading" /> |
||||
</template> |
||||
<script setup> |
||||
import { useRoute, useRouter } from "vue-router"; |
||||
import { authOpenid } from "@/api/auth"; |
||||
import UserStore from "@/store/user"; |
||||
import { setToken, getToken } from "@/util/cookie"; |
||||
|
||||
const router = useRouter(); |
||||
const route = useRoute(); |
||||
const userStore = UserStore(); |
||||
|
||||
const loading = ref(false); |
||||
|
||||
if (!getToken()) { |
||||
if (route.query.openid) { |
||||
// 自定义登录 |
||||
authOpenid(route.query.openid).then((data) => { |
||||
setToken(data.token); |
||||
userStore.user = data.user; |
||||
loading.value = false; |
||||
|
||||
wxStore.initSign(); |
||||
}); |
||||
} |
||||
} else { |
||||
loading.value = false; |
||||
|
||||
wxStore.initSign(); |
||||
} |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
.wrapper { |
||||
background-color: #fff; |
||||
} |
||||
header { |
||||
img { |
||||
width: 100%; |
||||
} |
||||
} |
||||
a { |
||||
width: 45.2%; |
||||
height: 84px; |
||||
text-decoration: none; |
||||
border: 1px solid var(--primary-color); |
||||
color: var(--primary-color); |
||||
font-weight: bold; |
||||
font-size: 18px; |
||||
&:hover { |
||||
cursor: pointer; |
||||
} |
||||
} |
||||
</style> |
||||
@ -0,0 +1,78 @@
|
||||
<template> |
||||
<el-form :model="form" label-width="120px"> |
||||
<el-form-item label="Activity name"> |
||||
<el-input v-model="form.name" /> |
||||
</el-form-item> |
||||
<el-form-item label="Activity zone"> |
||||
<el-select v-model="form.region" placeholder="please select your zone"> |
||||
<el-option label="Zone one" value="shanghai" /> |
||||
<el-option label="Zone two" value="beijing" /> |
||||
</el-select> |
||||
</el-form-item> |
||||
<el-form-item label="Activity time"> |
||||
<el-col :span="11"> |
||||
<el-date-picker |
||||
v-model="form.date1" |
||||
type="date" |
||||
placeholder="Pick a date" |
||||
style="width: 100%" |
||||
/> |
||||
</el-col> |
||||
<el-col :span="2" class="text-center"> |
||||
<span class="text-gray-500">-</span> |
||||
</el-col> |
||||
<el-col :span="11"> |
||||
<el-time-picker |
||||
v-model="form.date2" |
||||
placeholder="Pick a time" |
||||
style="width: 100%" |
||||
/> |
||||
</el-col> |
||||
</el-form-item> |
||||
<el-form-item label="Instant delivery"> |
||||
<el-switch v-model="form.delivery" /> |
||||
</el-form-item> |
||||
<el-form-item label="Activity type"> |
||||
<el-checkbox-group v-model="form.type"> |
||||
<el-checkbox label="Online activities" name="type" /> |
||||
<el-checkbox label="Promotion activities" name="type" /> |
||||
<el-checkbox label="Offline activities" name="type" /> |
||||
<el-checkbox label="Simple brand exposure" name="type" /> |
||||
</el-checkbox-group> |
||||
</el-form-item> |
||||
<el-form-item label="Resources"> |
||||
<el-radio-group v-model="form.resource"> |
||||
<el-radio label="Sponsor" /> |
||||
<el-radio label="Venue" /> |
||||
</el-radio-group> |
||||
</el-form-item> |
||||
<el-form-item label="Activity form"> |
||||
<el-input v-model="form.desc" type="textarea" /> |
||||
</el-form-item> |
||||
<el-form-item> |
||||
<el-button type="primary" @click="onSubmit">Create</el-button> |
||||
<el-button>Cancel</el-button> |
||||
</el-form-item> |
||||
</el-form> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import { reactive } from 'vue' |
||||
|
||||
// do not use same name with ref |
||||
const form = reactive({ |
||||
name: '', |
||||
region: '', |
||||
date1: '', |
||||
date2: '', |
||||
delivery: false, |
||||
type: [], |
||||
resource: '', |
||||
desc: '', |
||||
}) |
||||
|
||||
const onSubmit = () => { |
||||
console.log('submit!') |
||||
} |
||||
</script> |
||||
|
||||
@ -0,0 +1,9 @@
|
||||
<template> |
||||
<h1 class="text-center">404</h1> |
||||
</template> |
||||
<script setup> |
||||
|
||||
</script> |
||||
<style lang="scss" scoped> |
||||
|
||||
</style> |
||||
@ -0,0 +1,58 @@
|
||||
import { defineConfig } from 'vite' |
||||
import vue from '@vitejs/plugin-vue' |
||||
import AutoImport from 'unplugin-auto-import/vite' |
||||
import svgLoader from 'vite-svg-loader' |
||||
import path from 'path' |
||||
import Components from 'unplugin-vue-components/vite' |
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' |
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({ |
||||
|
||||
plugins: [ |
||||
vue(), |
||||
svgLoader(), |
||||
AutoImport({ |
||||
imports: [ |
||||
'vue', 'vue-router' |
||||
], |
||||
resolvers: [ElementPlusResolver()], |
||||
}), |
||||
Components({ |
||||
resolvers: [ElementPlusResolver({ |
||||
importStyle: "sass" |
||||
})], |
||||
}), |
||||
], |
||||
resolve: { |
||||
// https://cn.vitejs.dev/config/#resolve-alias
|
||||
alias: { |
||||
// 设置路径
|
||||
'~': path.resolve(__dirname, './'), |
||||
// 设置别名
|
||||
'@': path.resolve(__dirname, './src/') |
||||
}, |
||||
// https://cn.vitejs.dev/config/#resolve-extensions
|
||||
extensions: ['.js'] |
||||
}, |
||||
css: { |
||||
preprocessorOptions: { |
||||
scss: { |
||||
additionalData: `@use "src/assets/style/theme.scss" as *;` |
||||
}, |
||||
}, |
||||
}, |
||||
server: { |
||||
host: '0.0.0.0', |
||||
port: 5174, |
||||
proxy: { |
||||
'/api': { |
||||
// https://mailbox.biutag.com/api
|
||||
// http://127.0.0.1:8080
|
||||
target: 'https://mailbox.biutag.com/api', |
||||
changeOrigin: true, |
||||
rewrite: (p) => p.replace(/^\/api/, '') |
||||
} |
||||
} |
||||
} |
||||
}) |
||||
Loading…
Reference in new issue