commit
14223e89c5
@ -0,0 +1,8 @@ |
|||||||
|
node_modules |
||||||
|
.DS_Store |
||||||
|
dist |
||||||
|
dist-ssr |
||||||
|
*.local |
||||||
|
/yarn.lock |
||||||
|
*.log |
||||||
|
.idea |
@ -0,0 +1,12 @@ |
|||||||
|
FROM nginx:alpine |
||||||
|
|
||||||
|
ADD vue.app.conf /etc/nginx/conf.d/ |
||||||
|
|
||||||
|
ADD dist /app |
||||||
|
|
||||||
|
RUN mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.bak \ |
||||||
|
&& dos2unix /etc/nginx/conf.d/vue.app.conf |
||||||
|
|
||||||
|
EXPOSE 9000 |
||||||
|
|
||||||
|
ENTRYPOINT ["nginx","-g","daemon off;"] |
@ -0,0 +1,13 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8" /> |
||||||
|
<link rel="icon" href="/favicon.ico" /> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||||
|
<title>Vite App</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="app"></div> |
||||||
|
<script type="module" src="/src/main.ts"></script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,29 @@ |
|||||||
|
import {MockMethod} from 'vite-plugin-mock'; |
||||||
|
import {sendCodeApi} from "../src/interface"; |
||||||
|
|
||||||
|
|
||||||
|
const Mock = require('mockjs') |
||||||
|
|
||||||
|
export default [ |
||||||
|
{ |
||||||
|
url: sendCodeApi, |
||||||
|
method: 'post', |
||||||
|
response: ({ query }) => { |
||||||
|
return Mock.mock({ |
||||||
|
'result':'OK', |
||||||
|
'message':'验证码发送成功' |
||||||
|
}) |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
url: '/api/post', |
||||||
|
method: 'post', |
||||||
|
timeout: 2000, |
||||||
|
response: { |
||||||
|
code: 0, |
||||||
|
data: { |
||||||
|
name: 'vben', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
] as MockMethod[]; |
@ -0,0 +1,30 @@ |
|||||||
|
{ |
||||||
|
"name": "bililive_webapp", |
||||||
|
"version": "0.0.0", |
||||||
|
"scripts": { |
||||||
|
"dev": "vite", |
||||||
|
"build": "vuedx-typecheck . && vite build", |
||||||
|
"serve": "vite preview" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"element3": "^0.0.39", |
||||||
|
"mockjs": "^1.1.0", |
||||||
|
"vue": "^3.0.5", |
||||||
|
"vue-router": "4", |
||||||
|
"vuex": "^4.0.0" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@vitejs/plugin-vue": "^1.1.4", |
||||||
|
"@vue/compiler-sfc": "^3.0.5", |
||||||
|
"@vuedx/typecheck": "^0.6.0", |
||||||
|
"@vuedx/typescript-plugin-vue": "^0.6.0", |
||||||
|
"cross-env": "^7.0.3", |
||||||
|
"node-sass": "^5.0.0", |
||||||
|
"sass": "^1.32.7", |
||||||
|
"sass-loader": "^11.0.1", |
||||||
|
"style-loader": "^2.0.0", |
||||||
|
"typescript": "^4.1.3", |
||||||
|
"vite": "^2.0.0-beta.64", |
||||||
|
"vite-plugin-mock": "^2.1.4" |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,51 @@ |
|||||||
|
<template> |
||||||
|
<el-container> |
||||||
|
<el-header> |
||||||
|
<el-row> |
||||||
|
<el-col :span="4"> |
||||||
|
<h1>直播间状态通知</h1> |
||||||
|
</el-col> |
||||||
|
<el-col :span="20"> |
||||||
|
<el-menu mode="horizontal" :default-active="activeIndex"> |
||||||
|
<el-menu-item index="1"><router-link to="/">主页</router-link></el-menu-item> |
||||||
|
<el-submenu index="2"> |
||||||
|
<template v-slot:title>个人信息</template> |
||||||
|
<el-menu-item index="2-1">修改密码</el-menu-item> |
||||||
|
<el-menu-item index="2-2"><router-link to="/email">修改邮箱</router-link></el-menu-item> |
||||||
|
<el-menu-item index="2-3"><router-link to="">退出登录</router-link></el-menu-item> |
||||||
|
</el-submenu> |
||||||
|
</el-menu> |
||||||
|
</el-col> |
||||||
|
</el-row> |
||||||
|
</el-header> |
||||||
|
<el-main> |
||||||
|
<router-view></router-view> |
||||||
|
</el-main> |
||||||
|
</el-container> |
||||||
|
|
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
import {defineComponent, ref} from 'vue' |
||||||
|
|
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: 'App', |
||||||
|
setup(){ |
||||||
|
const activeIndex=ref<string>('1') |
||||||
|
|
||||||
|
return {activeIndex} |
||||||
|
} |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style> |
||||||
|
#app { |
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif; |
||||||
|
-webkit-font-smoothing: antialiased; |
||||||
|
-moz-osx-font-smoothing: grayscale; |
||||||
|
text-align: center; |
||||||
|
color: #2c3e50; |
||||||
|
margin-top: 60px; |
||||||
|
} |
||||||
|
</style> |
After Width: | Height: | Size: 6.7 KiB |
@ -0,0 +1,11 @@ |
|||||||
|
.mt{ |
||||||
|
&-1{ |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.btn-block{ |
||||||
|
display: block; |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,28 @@ |
|||||||
|
<template> |
||||||
|
<h1>一个异步组件</h1> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
import {defineComponent} from 'vue' |
||||||
|
|
||||||
|
function sleep(timeout: number | undefined){ |
||||||
|
return new Promise(resolve => setTimeout(resolve,timeout)) |
||||||
|
} |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "AsyncComponent", |
||||||
|
props:{ |
||||||
|
timeout:{ |
||||||
|
type:Number, |
||||||
|
required:true |
||||||
|
} |
||||||
|
}, |
||||||
|
async setup(props){ |
||||||
|
await sleep(props.timeout) |
||||||
|
} |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
|
||||||
|
</style> |
@ -0,0 +1,110 @@ |
|||||||
|
<template> |
||||||
|
<el-row type="flex" justify="center"> |
||||||
|
<el-col :span="8"> |
||||||
|
<el-input v-model="userEmail" :disabled="true"> |
||||||
|
<template v-slot:prepend>当前邮箱</template> |
||||||
|
</el-input> |
||||||
|
</el-col> |
||||||
|
</el-row> |
||||||
|
<el-row type="flex" justify="center" class="mt-1"> |
||||||
|
<el-col :span="8"> |
||||||
|
<el-input v-model="newEmail" :disabled="second>0"> |
||||||
|
<template v-slot:prepend>新邮箱</template> |
||||||
|
</el-input> |
||||||
|
</el-col> |
||||||
|
</el-row> |
||||||
|
<el-row type="flex" justify="center" class="mt-1"> |
||||||
|
<el-col :span="6"> |
||||||
|
<el-input v-model="code"> |
||||||
|
<template v-slot:prepend>验证码</template> |
||||||
|
</el-input> |
||||||
|
</el-col> |
||||||
|
<el-col :span="2"> |
||||||
|
<el-button type="primary" :disabled="!allowSendCode" @click="sendCode">{{ sendCodeTip }}</el-button> |
||||||
|
</el-col> |
||||||
|
</el-row> |
||||||
|
<el-row type="flex" justify="center" class="mt-1"> |
||||||
|
<el-col :span="2"> |
||||||
|
<el-button type="primary" class="btn-block" :disabled="!checkCode">更换邮箱</el-button> |
||||||
|
</el-col> |
||||||
|
<el-col :span="2"/> |
||||||
|
<el-col :span="2"> |
||||||
|
<el-button type="info" class="btn-block">返回</el-button> |
||||||
|
</el-col> |
||||||
|
</el-row> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
import {computed, defineComponent, ref} from "vue"; |
||||||
|
|
||||||
|
import {changeEmail as _changeEmail, sendCode as _sendCode} from "../request"; |
||||||
|
import {useStore} from "vuex"; |
||||||
|
import {Message} from 'element3/src/components/Message' |
||||||
|
|
||||||
|
export default defineComponent<{ userEmail: { require: boolean, type: StringConstructor }, second: { require: boolean, type: NumberConstructor } }>({ |
||||||
|
name: 'Email', |
||||||
|
setup(props) { |
||||||
|
const newEmail = ref<string>('') |
||||||
|
const code = ref<string>('') |
||||||
|
|
||||||
|
const second = ref<number>(0) |
||||||
|
|
||||||
|
const store=useStore() |
||||||
|
|
||||||
|
const sendCode = () => { |
||||||
|
second.value=10 |
||||||
|
_sendCode(store.state.user.email).then(res=>res.json()).then(res=>{ |
||||||
|
if(res.result==='OK'){ |
||||||
|
console.info('发送验证码成功') |
||||||
|
new Message({ |
||||||
|
showClose: true, |
||||||
|
message: '发送验证码成功', |
||||||
|
type: 'success' |
||||||
|
}) |
||||||
|
|
||||||
|
const t=setInterval(function (){ |
||||||
|
if(second.value===0){ |
||||||
|
clearInterval(t) |
||||||
|
}else { |
||||||
|
second.value-- |
||||||
|
} |
||||||
|
},1000) |
||||||
|
}else{ |
||||||
|
second.value=0 |
||||||
|
new Message({ |
||||||
|
showClose: true, |
||||||
|
message: '发送验证码失败', |
||||||
|
type: 'error' |
||||||
|
}) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
const changeEmail=()=> _changeEmail(store.state.user.email,newEmail.value,code.value) |
||||||
|
|
||||||
|
const allowSendCode = computed<boolean>(() => second.value === 0 && props.userEmail !== newEmail.value && /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(newEmail.value)) |
||||||
|
|
||||||
|
const sendCodeTip = computed<string>(() => second.value > 0 ? second.value + '秒后重发' : '发送验证码') |
||||||
|
|
||||||
|
const checkCode = computed<boolean>(() => /^\d{6}$/.test(code.value)) |
||||||
|
|
||||||
|
const userEmail= computed(() => store.state.user.email) |
||||||
|
|
||||||
|
return { |
||||||
|
newEmail, |
||||||
|
code, |
||||||
|
allowSendCode, |
||||||
|
sendCodeTip, |
||||||
|
checkCode, |
||||||
|
second, |
||||||
|
sendCode, |
||||||
|
userEmail, |
||||||
|
changeEmail |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
|
||||||
|
</style> |
@ -0,0 +1,13 @@ |
|||||||
|
<template> |
||||||
|
<h1>主页</h1> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
export default { |
||||||
|
name: "Home" |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
|
||||||
|
</style> |
@ -0,0 +1,49 @@ |
|||||||
|
<template> |
||||||
|
<h1>{{num}}*2={{double}}</h1> |
||||||
|
<h1>{{num2.count}}*3={{triple}}</h1> |
||||||
|
<button @click="add">累加</button> |
||||||
|
|
||||||
|
<button @click="insert">插入</button> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
import {computed, defineComponent, reactive, ref} from 'vue' |
||||||
|
|
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "Vue3", |
||||||
|
|
||||||
|
setup:()=>{ |
||||||
|
const num=ref<number>(1) |
||||||
|
|
||||||
|
const isAdd=ref<boolean>(false) |
||||||
|
|
||||||
|
const num2=reactive<{count:number}>({ |
||||||
|
count:1 |
||||||
|
}) |
||||||
|
|
||||||
|
function add() { |
||||||
|
num.value++ |
||||||
|
num2.count++ |
||||||
|
} |
||||||
|
|
||||||
|
function insert(){ |
||||||
|
isAdd.value=true |
||||||
|
} |
||||||
|
|
||||||
|
function cancel(){ |
||||||
|
isAdd.value=false |
||||||
|
} |
||||||
|
|
||||||
|
const double=computed<number>(()=>num.value*2) |
||||||
|
|
||||||
|
const triple=computed<number>(()=>num2.count*3) |
||||||
|
|
||||||
|
return {num,add,double,num2,triple,isAdd,insert,cancel} |
||||||
|
} |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
|
||||||
|
</style> |
@ -0,0 +1 @@ |
|||||||
|
export const sendCodeApi='/api/change/email' |
@ -0,0 +1,11 @@ |
|||||||
|
import {createApp} from 'vue' |
||||||
|
// TypeScript error? Run VSCode command
|
||||||
|
// TypeScript: Select TypeScript version - > Use Workspace Version
|
||||||
|
import App from './App.vue' |
||||||
|
import Element3 from 'element3' |
||||||
|
import 'element3/lib/theme-chalk/index.css' |
||||||
|
import './assets/scss/style.scss' |
||||||
|
import {store} from "./store"; |
||||||
|
import {router} from "./router"; |
||||||
|
|
||||||
|
createApp(App).use(router).use(store).use(Element3).mount('#app') |
@ -0,0 +1,13 @@ |
|||||||
|
import {sendCodeApi} from "./interface"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 发送验证码 |
||||||
|
* @param userEmail |
||||||
|
*/ |
||||||
|
export function sendCode(userEmail:string){ |
||||||
|
return fetch(new Request(sendCodeApi,{method:'POST',body:JSON.stringify({userEmail})})) |
||||||
|
} |
||||||
|
|
||||||
|
export function changeEmail(oldEmail:string,newEmail:string,code:string){ |
||||||
|
|
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
// 1. 定义路由组件.
|
||||||
|
// 也可以从其他文件导入
|
||||||
|
import {createRouter, createWebHashHistory} from "vue-router"; |
||||||
|
import Email from "./components/Email.vue"; |
||||||
|
import Home from "./components/Home.vue"; |
||||||
|
|
||||||
|
// 2. 定义一些路由
|
||||||
|
// 每个路由都需要映射到一个组件。
|
||||||
|
// 我们后面再讨论嵌套路由。
|
||||||
|
const routes = [ |
||||||
|
{path:'/',component: Home}, |
||||||
|
{ path: '/email', component: Email }, |
||||||
|
] |
||||||
|
|
||||||
|
// 3. 创建路由实例并传递 `routes` 配置
|
||||||
|
// 你可以在这里输入更多的配置,但我们在这里
|
||||||
|
// 暂时保持简单
|
||||||
|
export const router = createRouter({ |
||||||
|
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
|
||||||
|
history: createWebHashHistory(), |
||||||
|
routes, // `routes: routes` 的缩写
|
||||||
|
}) |
@ -0,0 +1,16 @@ |
|||||||
|
import {createStore} from "vuex"; |
||||||
|
|
||||||
|
export const store = createStore({ |
||||||
|
state: () => ({ |
||||||
|
user: { |
||||||
|
email: '1029559041@qq.com' |
||||||
|
} |
||||||
|
}), |
||||||
|
mutations: { |
||||||
|
setUser(state, user) { |
||||||
|
// @ts-ignore
|
||||||
|
state.user = user |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}) |
@ -0,0 +1,14 @@ |
|||||||
|
{ |
||||||
|
"compilerOptions": { |
||||||
|
"target": "esnext", |
||||||
|
"module": "esnext", |
||||||
|
"moduleResolution": "node", |
||||||
|
"strict": true, |
||||||
|
"jsx": "preserve", |
||||||
|
"sourceMap": true, |
||||||
|
"lib": ["esnext", "dom"], |
||||||
|
"types": ["vite/client"], |
||||||
|
"plugins": [{ "name": "@vuedx/typescript-plugin-vue" }] |
||||||
|
}, |
||||||
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
import {defineConfig} from 'vite' |
||||||
|
import vue from '@vitejs/plugin-vue' |
||||||
|
import {viteMockServe} from "vite-plugin-mock"; |
||||||
|
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({ |
||||||
|
plugins: [vue(),viteMockServe({})] |
||||||
|
}) |
@ -0,0 +1,9 @@ |
|||||||
|
server { |
||||||
|
listen 9000; |
||||||
|
|
||||||
|
location / { |
||||||
|
root /app; |
||||||
|
index index.html; |
||||||
|
try_files $uri $uri/ /index.html last; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue