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