This commit is contained in:
2025-07-16 10:14:15 +08:00
commit 9b845f1532
41 changed files with 3274 additions and 0 deletions

17
App.vue Normal file
View File

@ -0,0 +1,17 @@
<script>
export default {
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style lang="scss">
/* 注意要写在第一行同时给style标签加入lang="scss"属性 */
@import "uview-ui/index.scss";
</style>

17
api/addexam/index.js Normal file
View File

@ -0,0 +1,17 @@
import request from "../request.js"
export function getExamList() {
return request({
url: `/exam/api/exam/exam/getExamList?examType=2&userId=${uni.getStorageSync('examh5user').id}`,
method: 'get',
})
}
export function save(data) {
return request({
url: `/exam/api/exam/registration/save`,
method: 'post',
data
})
}

4
api/baseurl.js Normal file
View File

@ -0,0 +1,4 @@
var baseurl = "http://8.131.93.145:54012";
export default baseurl

8
api/examlist/index.js Normal file
View File

@ -0,0 +1,8 @@
import request from "../request.js"
export function getRegExamList() {
return request({
url: `/exam/api/exam/registration/getRegExamList?userId=${uni.getStorageSync('examh5user').id}`,
method: 'get',
})
}

10
api/login/index.js Normal file
View File

@ -0,0 +1,10 @@
import request from "../request.js"
//注册
export function login(data) {
return request({
url: `/exam/api/sys/user/login`,
method: 'post',
data
})
}

10
api/register/index.js Normal file
View File

@ -0,0 +1,10 @@
import request from "../request.js"
//注册
export function reg(data) {
return request({
url: `/exam/api/sys/user/reg`,
method: 'post',
data
})
}

53
api/request.js Normal file
View File

@ -0,0 +1,53 @@
import baseurl from './baseurl.js'
var request = function(config) {
return new Promise((resolve, rejected) => {
uni.showLoading({
title: ''
});
uni.request({
url: baseurl + config.url,
data: config.data,
method: config.method,
timeout: 10000,
header: {
...config.header,
token: uni.getStorageSync('examh5token')
},
success(res) {
uni.hideLoading();
if (res.data.code == 0) {
resolve(res.data)
} else if (res.data.code == 401 || res.data.code == 10010002 || res.data.code ==
90010001 || res.data.code == 90010002 || res.data.code == 90010005) {
uni.showModal({
title: '提示',
content: res.data.msg,
showCancel: false,
success: function(res) {
if (res.confirm) {
uni.clearStorageSync();
uni.reLaunch({
url: "/pages/login/login"
})
}
}
});
} else {
uni.showModal({
title: '提示',
showCancel: false,
content: res.data.msg,
success: function(res) {}
});
}
},
fail(err) {
uni.hideLoading();
rejected(err)
}
})
})
}
export default request

12
api/user/index.js Normal file
View File

@ -0,0 +1,12 @@
import request from "../request.js"
export function info(data) {
return request({
url: `/exam/api/sys/user/info`,
method: 'post',
header: {
'Content-Type': 'application/x-www-form-urlencoded',
},
data
})
}

View File

@ -0,0 +1,52 @@
<template>
<view class="signature" style="background-color: #F4F5F7;height: 800rpx;">
<Signature @init="onSignInit" style='background-color: #fff;height: 700rpx;width: 100%;'></Signature>
<view class="btns">
<button @click="clear">清空</button>
<button @click="revoke">撤回</button>
<button @click="saveTempFilePath">保存</button>
</view>
</view>
</template>
<script>
import Signature from '@/components/v-sign/v-sign.vue'
export default {
components: {
Signature
},
methods: {
onSignInit(signCtx) {
this.signCtx = signCtx
},
//
clear() {
this.signCtx.clear()
},
//
revoke() {
this.signCtx.revoke()
},
// h5 base64
async saveTempFilePath() {
const res = await this.signCtx.canvasToTempFilePath()
this.$emit('userSignaturePictureUrl', res)
},
}
}
</script>
<style lang='scss'>
.signature {
height: 800rpx;
position: relative;
}
.btns {
margin-top: 50rpx;
display: flex;
}
button {
width: 30%;
}
</style>

View File

@ -0,0 +1,15 @@
/**
* 判断是否未数值
* @param {Object} val
*/
export function isNumber(val) {
return !isNaN(Number(val))
}
/**
* 处理大小单位
* @param {Object} val
*/
export function formatSize(val) {
return isNumber(val) ? `${val}rpx` : val
}

View File

@ -0,0 +1,162 @@
<template>
<view class="v-sign-action" :style="[customStyle]">
<view
v-for="item in btns"
:key="item.label"
:class="['btn', { border: border }]"
:style="[{ 'margin-right': formatSize(space) }]"
@click="onBtnClick(item)"
>
<image :class="['icon', 'icon-' + item.action]" :src="item.icon"></image>
<text class="text">{{ item.label }}</text>
</view>
</view>
</template>
<script>
/**
* v-sign-action 控制按钮组v-sign 子组件
* @description 控制 v-sign 组件的一些按钮
* @tutorial
* @property {Array} actions 按钮配置 所有值 清空clear, 撤回prev 保存图片save
* @property {Boolean} border 按钮是否有边框
* @property {String/Number} space 按钮间隔
* @property {Object} customStyle 根元素自定义样式
* @event {Function} 点击对应类型按钮触发对应事件 例如点击 clear 则触发 clear 事件
* @example 示例
**/
import { formatSize } from './utils'
// v-sign
let vSignInterface
//
const btn_type = {
CLEAR: 'clear', //
PREV: 'prev', // /
// NEXT: 'next',
SAVE: 'save' //
}
const all_action = Object.values(btn_type)
const btnsConf = [
{
label: '清空',
action: btn_type.CLEAR,
icon: '/static/v-sign/clear.png'
},
{
label: '撤回',
action: btn_type.PREV,
icon: '/static/v-sign/prev.png'
},
// {
// label: '',
// action: btn_type.NEXT,
// icon: '/static/v-sign/next.png'
// },
{
label: '保存',
action: btn_type.SAVE,
icon: '/static/v-sign/save.png'
}
]
export default {
name: 'v-sign-action',
props: {
//
actions: {
type: Array,
default: () => all_action
},
//
border: {
type: Boolean,
default: true
},
//
space: {
type: [String, Number],
default: 12
},
//
customStyle: {
type: Object,
default: () => ({})
}
},
inject: ['getInterface'],
data() {
return {
formatSize
}
},
computed: {
btns() {
return btnsConf.filter(item => this.actions.includes(item.action))
}
},
mounted() {
vSignInterface = this.getInterface()
},
methods: {
async onBtnClick(btn) {
// console.log(btn, btn.action)
let emit_result
switch (btn.action) {
case btn_type.CLEAR:
vSignInterface.clear()
break
case btn_type.PREV:
vSignInterface.revoke()
break
// case btn_type.NEXT:
// console.log('next')
// break
case btn_type.SAVE:
emit_result = await vSignInterface.canvasToTempFilePath()
break
default:
break
}
// console.log(btn.action, emit_result);
//
this.$emit(btn.action, emit_result)
}
}
}
</script>
<style lang="scss" scoped>
.v-sign-action {
display: flex;
flex-wrap: wrap;
.btn {
display: flex;
align-items: center;
padding: 0 12rpx;
min-width: 88rpx;
white-space: nowrap;
&:last-child {
margin-right: 0;
}
&.border {
border: 2rpx solid #666;
border-radius: 12rpx;
}
.icon {
width: 28rpx;
height: 28rpx;
&.icon-clear,
&.icon-prev,
&.icon-next {
margin-right: 4rpx;
}
&.icon-save {
}
}
.text {
color: #666;
font-size: 28rpx;
}
}
}
</style>

View File

@ -0,0 +1,211 @@
<template>
<view class="v-sign-pen">
<view class="label" v-if="label">{{ label }}</view>
<view class="options">
<view
class="opt-item"
:style="{
minHeight: minWrapHeight,
marginRight: space + 'rpx'
}"
v-for="item in csizes"
:key="item.size"
@click="onItemClick(item)"
>
<view
:class="type"
:style="{
border:
border && currentSelect.size === item.size
? `${borderWidth}rpx solid ${activeColor}`
: ''
}"
>
<view class="inner" :style="[defaultInnerStyle(item)]"></view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* v-sign-pen 画笔v-sign 子组件
* @description 控制 v-sign 画笔的线宽
* @tutorial
* @property {String} type 选项样式 line / circle
* @property {String} label 标签
* @property {Array} sizes 画笔大小数组单位 px
* @property {String} color 选项颜色
* @property {String} activeColor 选中项颜色
* @property {Boolean} border 选中项是否有边框
* @property {Number} borderWidth 边框大小单位 rpx
* @property {String} space 选项间隙单位 rpx
* @property {Number} bigger 圆点变大变粗倍数
* @property {Number} minSize 圆点最小大小单位 px
* @event {Function} change 选择画笔大小时触发
* @example
**/
// v-sign
let vSignInterface
//
const type_style = {
CIRCLE: 'circle',
LINE: 'line'
}
export default {
name: 'v-sign-pen',
props: {
//
type: {
type: String,
default: type_style.CIRCLE
},
label: {
type: String
},
// px
sizes: {
type: Array,
default: () => [2, 4, 6, 8, 10]
},
//
color: {
type: String,
default: '#333'
},
//
activeColor: {
type: String,
default: '#333'
},
//
border: {
type: Boolean,
default: true
},
// , rpx
borderWidth: {
type: Number,
default: 4
},
// , rpx
space: {
type: Number,
default: 20
},
//
bigger: {
type: Number,
default: 2
},
// px
minSize: {
type: Number,
default: 4
}
},
inject: ['getInterface'],
data() {
return {
type_style,
currentSelect: null,
csizes: [],
maxSize: 0,
maxCsize: 0
}
},
computed: {
minWrapHeight() {
let height
switch (this.type) {
case type_style.CIRCLE:
height = this.maxCsize + 10 + 'px'
break
case type_style.LINE:
height = this.maxSize + 4 + 'px'
break
}
return height
}
},
created() {
this.csizes = this.sizes.map((size, index) => {
const csize = (index + 1) * this.bigger + this.minSize
this.maxSize = csize > this.maxSize ? csize : this.maxSize
this.maxCsize = csize > this.maxCsize ? csize : this.maxCsize
return {
size,
csize
}
})
this.currentSelect = this.csizes[0]
},
mounted() {
vSignInterface = this.getInterface()
this.setLineWidth()
},
methods: {
onItemClick(opt) {
this.currentSelect = opt
this.setLineWidth()
this.$emit('change', opt.size)
},
setLineWidth() {
vSignInterface.setLineWidth(this.currentSelect.size)
},
defaultInnerStyle(item) {
let width
let height
switch (this.type) {
case type_style.CIRCLE:
width = `${item.csize}px`
height = `${item.csize}px`
break
case type_style.LINE:
width = '20px'
height = `${item.size}px`
break
}
const background = this.currentSelect.size === item.size ? this.activeColor : this.color
return {
width,
height,
background
}
}
}
}
</script>
<style lang="scss" scoped>
.v-sign-pen {
padding: 12rpx;
.label {
font-size: 28rpx;
color: #333;
}
.options {
display: flex;
align-items: flex-end;
.opt-item {
display: flex;
align-items: flex-end;
justify-content: center;
&:last-child {
margin-right: 0;
}
.circle {
border-radius: 50%;
padding: 4rpx;
.inner {
border-radius: 50%;
}
}
.line {
padding: 4rpx;
}
}
}
}
</style>

View File

@ -0,0 +1,238 @@
<template>
<view class="signature-wrap">
<canvas
:canvas-id="cid"
:id="cid"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
style="height:600rpx"
:style="[{ width: formatSize(width)}, customStyle]"
></canvas>
<slot />
</view>
</template>
<script>
/**
* sign canvas 手写签名
* @description 设置线条宽度颜色撤回清空
* @tutorial
* @property {String} cid canvas id 不设置则默认为 v-sign-时间戳
* @property {String, Number} width canvas 宽度
* @property {String, Number} height canvas 高度
* @property {Object} customStyle 自定义样式
* @property {String} lineColor 画笔颜色
* @property {Number} lineWidth 画笔大小权重大于 v-sign-pen 组件设置的画笔大小
* @event {Function} init 当创建完 canvas 实例后触发向外提供 canvas实例撤回清空方法
* @example <v-sign @init="signInit"></v-sign>
*/
import { formatSize } from './utils'
// convas
let canvasCtx
export default {
name: 'v-sign',
props: {
// canvas id
cid: {
type: String,
default: `v-sign-${Date.now()}`
// required: true
},
// canvas
width: {
type: [String, Number],
default: '100%'
},
// canvas
height: {
type: [String, Number],
default: 300
},
// v-sign-pen
lineWidth: {
type: Number
},
// 线
lineColor: {
type: String,
default: '#000'
},
// canvas
customStyle: {
type: Object,
default: () => ({})
}
},
provide() {
return {
getInterface: this.provideInterface
}
},
data() {
return {
formatSize,
lineData: [],
winWidth: 0,
winHeight: 0,
penLineWidth: null, // v-sign-pen
}
},
mounted() {
canvasCtx = uni.createCanvasContext(this.cid, this)
//
this.$emit('init', this.provideInterface())
//
uni.getSystemInfo({
success: res => {
this.winWidth = res.windowWidth
this.winHeight = res.windowHeight
}
})
},
methods: {
onTouchStart(e) {
const pos = e.touches[0]
this.lineData.push({
style: {
color: this.lineColor,
width: this.lineWidth || this.penLineWidth || 4
},
//
coordinates: [
{
type: e.type,
x: pos.x,
y: pos.y
}
]
})
this.drawLine()
},
onTouchMove(e) {
const pos = e.touches[0]
this.lineData[this.lineData.length - 1].coordinates.push({
type: e.type,
x: pos.x,
y: pos.y
})
this.drawLine()
},
onTouchEnd(e) {
// console.log(e.type, e)
},
//
clear() {
this.lineData = []
canvasCtx.clearRect(0, 0, this.winWidth, this.winHeight)
canvasCtx.draw()
},
//
revoke() {
this.lineData.pop()
this.lineData.forEach((item, index) => {
canvasCtx.beginPath()
canvasCtx.setLineCap('round')
canvasCtx.setStrokeStyle(item.style.color)
canvasCtx.setLineWidth(item.style.width)
item.coordinates.forEach(pos => {
if (pos.type == 'touchstart') {
canvasCtx.moveTo(pos.x, pos.y)
} else {
canvasCtx.lineTo(pos.x, pos.y)
}
})
canvasCtx.stroke()
})
canvasCtx.draw()
},
// 线
drawLine() {
const lineDataLen = this.lineData.length
if (!lineDataLen) return
const currentLineData = this.lineData[lineDataLen - 1]
const coordinates = currentLineData.coordinates
const coordinatesLen = coordinates.length
if (!coordinatesLen) return
let startPos
let endPos
if (coordinatesLen < 2) {
// only start, no move event
startPos = coordinates[coordinatesLen - 1]
endPos = { x: startPos.x + 1, y: startPos.y }
} else {
startPos = coordinates[coordinatesLen - 2]
endPos = coordinates[coordinatesLen - 1]
}
const style = currentLineData.style
canvasCtx.beginPath()
canvasCtx.setLineCap('round')
canvasCtx.setStrokeStyle(style.color)
canvasCtx.setLineWidth(style.width)
canvasCtx.moveTo(startPos.x, startPos.y)
canvasCtx.lineTo(endPos.x, endPos.y)
// const P1 = this.caculateBezier(startPos, endPos, centerPos)
// console.log(P1.x, P1.y)
// canvasCtx.moveTo(startPos.x, startPos.y)
// canvasCtx.quadraticCurveTo(P1.x, P1.y, endPos.x, endPos.y)
canvasCtx.stroke()
canvasCtx.draw(true)
},
canvasToTempFilePath(conf = {}) {
return new Promise((resolve, reject) => {
uni.canvasToTempFilePath(
{
canvasId: this.cid,
...conf,
success: res => {
resolve(res.tempFilePath)
},
fail: err => {
console.log('fail', err)
reject(err)
}
},
this
)
})
},
setLineWidth(numberVal) {
this.penLineWidth = numberVal
},
provideInterface() {
return {
cid: this.cid,
ctx: canvasCtx,
clear: this.clear,
revoke: this.revoke,
canvasToTempFilePath: this.canvasToTempFilePath,
setLineWidth: this.setLineWidth
}
},
/**
* 计算二次贝塞尔曲线 控制点 P1
* 起点 P0(x0,y0)控制点P1(x1, y1)P2(x2, y2)曲线上任意点B(x, y)
* 二次贝塞尔公式B(t) = (1-t)²P0 + 2t(1-t)P1 + t²P2
* 代入坐标得
* x = (1-t)²*x0 + 2t(1-t)*x1 + *x2
* y = (1-t)²*y0 + 2t(1-t)*y1 + *y2
*/
caculateBezier(P0, P2, B, t = 0.5) {
const { x: x0, y: y0 } = P0
const { x: x2, y: y2 } = P2
const { x, y } = B
let x1 = (x - (1 - t) * (1 - t) * x0 - t * t * x2) / (2 * t * (1 - t))
let y1 = (y - (1 - t) * (1 - t) * y0 - t * t * y2) / (2 * t * (1 - t))
return { x: x1, y: y1 }
}
}
}
</script>
<style lang="scss" scoped>
.signature-wrap {
position: relative;
}
</style>

BIN
homepage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

20
index.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

24
main.js Normal file
View File

@ -0,0 +1,24 @@
import App from './App'
// #ifndef VUE3
import Vue from 'vue'
import uView from "uview-ui";
Vue.use(uView);
import './uni.promisify.adaptor'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif

72
manifest.json Normal file
View File

@ -0,0 +1,72 @@
{
"name" : "exam-h5",
"appid" : "__UNI__254C48E",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2"
}

28
package-lock.json generated Normal file
View File

@ -0,0 +1,28 @@
{
"name": "exam-h5",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "exam-h5",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"uview-ui": "^1.8.8"
}
},
"node_modules/uview-ui": {
"version": "1.8.8",
"resolved": "https://registry.npmjs.org/uview-ui/-/uview-ui-1.8.8.tgz",
"integrity": "sha512-Osal3yzXiHor0In9OPTZuXTaqTbDglMZ9RGK/MPYDoQQs+y0hrBCUD0Xp5T70C8i2lLu2X6Z11zJhmsQWMR7Jg=="
}
},
"dependencies": {
"uview-ui": {
"version": "1.8.8",
"resolved": "https://registry.npmjs.org/uview-ui/-/uview-ui-1.8.8.tgz",
"integrity": "sha512-Osal3yzXiHor0In9OPTZuXTaqTbDglMZ9RGK/MPYDoQQs+y0hrBCUD0Xp5T70C8i2lLu2X6Z11zJhmsQWMR7Jg=="
}
}
}

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "exam-h5",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"uview-ui": "^1.8.8"
}
}

73
pages.json Normal file
View File

@ -0,0 +1,73 @@
{
"easycom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
},
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录",
"enablePullDownRefresh": false
}
}, {
"path": "pages/examlist/examlist",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom",
"onReachBottomDistance": 40, // px
"enablePullDownRefresh": true //true
}
}, {
"path": "pages/user/user",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
}, {
"path": "pages/register/register",
"style": {
"navigationBarTitleText": "注册账号",
"enablePullDownRefresh": false
}
}, {
"path": "pages/forgotPassword/forgotPassword",
"style": {
"navigationBarTitleText": "忘记密码",
"enablePullDownRefresh": false
}
}, {
"path": "pages/addexam/addexam",
"style": {
"navigationBarTitleText": "新增考试",
"enablePullDownRefresh": false
}
}
],
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#4C7BC9",
"backgroundColor": "#4C7BC9"
},
"uniIdRouter": {},
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#435950",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/examlist/examlist",
"iconPath": "static/homepagew.png",
"selectedIconPath": "static/homepagews.png",
"text": "首页"
},
{
"pagePath": "pages/user/user",
"iconPath": "static/userw.png",
"selectedIconPath": "static/users.png",
"text": "我的"
}
]
}
}

379
pages/addexam/addexam.scss Normal file
View File

@ -0,0 +1,379 @@
.app {
font-size: 34rpx;
padding-top: 10rpx;
height: 100%;
background: #FFFFFF;
padding-bottom: 200rpx;
.title {
height: 100rpx;
line-height: 100rpx;
font-size: 34rpx;
margin: 0px auto;
width: 100%;
font-weight: 600;
text-align: center;
}
.mask {
.Agreement {
width: 100%;
background-color: #fff;
text-align: center;
height: 1000rpx;
position: absolute;
top: 5%;
width: 94%;
left: 3%;
font-size: 30rpx;
.scroll-Y {
width: 98%;
margin: 0 auto;
height: 830rpx;
overflow-y: scroll;
text-align: left;
text-indent: 2em;
}
.cancel {
height: 70rpx;
line-height: 70rpx;
font-size: 32rpx;
background-color: #F4F5F7;
position: absolute;
border-top: 1rpx solid #000000;
bottom: 0;
right: 0;
width: 50%;
color: #000000;
}
.determine {
height: 70rpx;
line-height: 70rpx;
font-size: 32rpx;
width: 50%;
color: #F4F5F7;
background: #4C7BC9;
position: absolute;
bottom: 0;
left: 0;
}
}
}
.addressitem {
width: 97%;
border-bottom: 1rpx solid #D8D4D4;
margin-left: 3%;
height: 100%;
.leftaddress {
width: 15%;
height: 100%;
display: flex;
display: inline-block;
}
.addition {
color: #666666;
display: inline-block;
line-height: 50rpx;
font-size: 28rpx;
}
}
.money {
font-size: 32rpx;
color: #D43953;
position: absolute;
right: 5%;
top: 15%;
}
.user {
width: 100%;
padding: 10rpx;
line-height: 93rpx;
margin-bottom: 10rpx;
.timeyear{
.bottomtext{
-webkit-overflow-scrolling: touch;
width: 96%;
margin: 0 auto;
.items{
display: flex;
justify-content:flex-start;
flex-wrap: wrap;
text-align: center;
.timeitem{
width: 21%;
margin: 0 2% 20rpx;
height: 86rpx;
border-radius: 5rpx;
font-size: 28rpx;
line-height: 86rpx;
border: 2rpx solid #DADADA;
}
.timeitemtap{
width: 21%;
margin: 0 2% 20rpx;
height: 86rpx;
border: 2rpx solid #F44B2F;
border-radius: 5rpx;
font-size: 28rpx;
color: #F44B2F;
line-height: 86rpx;
}
}
}
.toptext{
-webkit-overflow-scrolling: touch;
padding-top: 10rpx;
display: flex;
justify-content:space-around;
height: 50rpx;
color: #000000;
line-height: 27rpx;
margin-bottom: 30rpx;
text-align: center;
.Soonerorlater{
width: 200rpx;
font-size: 36rpx;
height: 50rpx;
}
.Soonerorlaterclass{
font-size: 36rpx;
color: #F44B2F;
width: 200rpx;
height: 50rpx;
}
view:nth-child(2){
width: 4rpx;
height: 27rpx;
background: #C5C3C3;
border-radius: 2rpx;
}
}
}
.item {
width: 97%;
border-bottom: 1rpx solid #D8D4D4;
margin-left: 3%;
.addition {
color: #666666;
line-height: 30rpx;
font-size: 28rpx;
}
}
}
.remarks {
width: 94%;
margin: 10rpx auto;
padding: 3%;
font-size: 34rpx;
height: 96rpx;
background: #FFFFFF;
border-radius: 20rpx;
margin-top: 20rpx;
position: relative;
.span {
display: inline-block;
width: 20%;
// overflow: hidden;
// text-overflow: ellipsis;
// white-space: nowrap;
position: absolute;
top:50%;
transform: translateY(-50%);
}
.input-placeholder{
color: #C5C3C3;
font-size: 30rpx;
}
input {
position: absolute;
top: 50%;
left: 23%;
width: 77%;
transform: translateY(-50%);
}
}
.priceback {
width: 90%;
background: #FFFFFF;
height: 100rpx;
margin: 0 auto;
.queren {
width: 100%;
height: 90rpx;
background: #4C7BC9;
border-radius: 26rpx;
margin: 105rpx 0 0 15rpx;
line-height: 90rpx;
text-align: center;
color: #ffffff;
}
}
.radio-content {
margin: 50rpx auto;
width: 70%;
text-align: center;
font-size: 28rpx;
position: relative;
.agreement {
position: absolute;
top: 50%;
left: 20%;
transform: translateY(-50%);
color: #878987;
}
.radio-right {
height: 100rpx;
width: 100rpx;
.radio {
display: inline-block;
width: 35rpx;
height: 35rpx;
border-radius: 70%;
border: 2rpx solid #178ffb;
position: absolute;
top: 50%;
left: 5%;
transform: translateY(-50%);
.radio-active {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background-color: #178ffb;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
// margin: 0 auto;
}
}
}
}
.Consumablespackage .money {
top: 8%;
}
/* 耗材包 */
.Consumablespackage {
width: 94%;
margin: 10rpx auto;
padding: 3% 0 3% 3%;
font-size: 34rpx;
background: #FFFFFF;
border-radius: 20rpx;
margin-top: 20rpx;
position: relative;
padding-bottom: 40rpx;
::v-deep .u-checkbox__label{
width: 100% !important;
}
.detail {
width: 100%;
line-height: 100rpx;
// padding: 20rpx 0 0 40rpx;
position: relative;
.itemConsumabletitle{
width:55%;
position: absolute;
top:50%;
transform: translateY(-50%);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
font-size: 28rpx;
}
/deep/ .u-checkbox{
margin: 40rpx 0 0;
position: relative;
}
span{
width: 100%;
display: inline-block;
font-size: 28rpx;
color: #D43953;
text-align: right;
position: absolute;
right:5%;
top:50%;
transform: translateY(-50%);
}
}
}
/* 套餐 */
.Package {
width: 97%;
height: 500rpx;
font-size: 34rpx;
background: #FFFFFF;
border-radius: 20rpx;
margin: 20rpx auto 40rpx;
position: relative;
.uppicture {
border: 1rpx dashed #818181;
width: 90%;
height: 400rpx;
margin: 0 auto;
position: relative;
image {
width: 100%;
height: 100%;
}
span {
font-size: 35rpx;
color: #969394;
}
}
.checkboxs{
margin-top: 30rpx;
::v-deep .u-checkbox{
padding-right: 40rpx;
}
}
.detail {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 31rpx;
font-size: 32rpx;
color: #666666;
line-height: 31rpx;
position: absolute;
left: 10%;
top: 60%;
width: 90%;
}
}
.message {
width: 94%;
background: white;
border-radius: 20rpx;
margin: 0 auto;
text-align: center;
line-height: 96rpx;
}
}

503
pages/addexam/addexam.vue Normal file
View File

@ -0,0 +1,503 @@
<template>
<view class="app">
<view class="user">
<view class="title">
个人信息
</view>
<view class="item">
<span>姓名</span>
<span class='addition'>{{personInfo.realName}}</span>
</view>
<view class="item">
<span>手机号</span>
<span class='addition'>{{personInfo.phone}}</span>
</view>
<view class="item">
<span>身份证号</span>
<span class='addition'>{{personInfo.userName}}</span>
</view>
<view class="item">
<span>性别</span>
<span class='addition'>{{personInfo.sex}}</span>
</view>
<view class="item">
<span>出生日期</span>
<span class='addition'>{{personInfo.birthday}}</span>
</view>
<view class="item">
<span>电子邮箱(用于接收考试通知)</span>
<span class='addition'>
<u-input type="text" placeholder="请输入电子邮箱" v-model="personInfo.email" />
</span>
</view>
<view class="item">
<span>通讯地址</span>
<span class='addition'>
<u-input type="text" placeholder="请输入通讯地址" v-model="personInfo.address" />
</span>
</view>
</view>
<view class="user">
<view class="title">
教育背景
</view>
<view class="item">
<span>最高学历</span>
<span class='addition'>
<u-radio-group v-model="personInfo.education" @change="">
<u-radio @change="" v-for="(item, index) in educationlist" :key="index" :name="item.label"
style="padding: 0 15rpx;" :disabled="item.checked">
{{item.name}}
</u-radio>
</u-radio-group>
</span>
</view>
<view class="item">
<span>毕业院校</span>
<span class='addition'>
<u-input type="text" placeholder="请输入毕业院校" v-model="personInfo.graduateSchool" />
</span>
</view>
<view class="item">
<span>专业(如适用)</span>
<span class='addition'>
<u-input type="text" placeholder="请输入专业" v-model="personInfo.major" />
</span>
</view>
</view>
<view class="user">
<view class="title">
报考信息
<span class='addition' @click="examshow=true"
style="margin-left:20rpx;padding:6rpx 20rpx;background: #4C7BC9;border-radius: 10rpx;color: #fff;">
选择考试
</span>
</view>
<view class="item">
<span>考试名称</span>
<span class='addition' v-if="personInfo.title">
{{personInfo.title}}
</span>
</view>
<view class="item">
<span>考试日期</span>
<span class='addition' v-if="personInfo.title">
{{personInfo.startDate}}{{personInfo.endDate}}
</span>
</view>
<view class="item">
<span>考试时间</span>
<span class='addition' v-if="personInfo.title">
{{personInfo.startTime}}{{personInfo.endTime}}
</span>
</view>
<view class="item">
<span>考试费用</span>
<span class='addition' v-if="personInfo.title" style="color:red">
{{personInfo.examFee}}
</span>
</view>
<view class="item">
<span>报考类别(可选)</span>
<span class='addition'>
<u-radio-group v-model="personInfo.regType" @change="">
<u-radio @change="" v-for="(item, index) in regTypelist" :key="index" :name="item.label"
style="padding: 15rpx;" :disabled="item.checked">
{{item.name}}
</u-radio>
</u-radio-group>
</span>
</view>
<view class="item">
<span>培训经历(如有)</span>
<span class='addition'>
<view class="item" style="padding: 10rpx 0;">
<span>机构名称</span>
<span class='addition'>
<u-input type="text" placeholder="请输入机构名称" v-model="personInfo.trainInstitution" />
</span>
</view>
<view class="item" style="padding: 10rpx 0;">
<span>培训时间</span>
<span class='addition' @click="trainDateshow=true"
style="margin-left:50rpx;display: inline-block;padding: 10rpx;background-color: #4C7BC9;border-radius: 10rpx;color: #fff;">
选择时间区间
</span>
<view class="" v-if="personInfo.trainStartDate" style="padding: 16rpx 0;">
{{personInfo.trainStartDate+'至'}}{{personInfo.trainEndDate}}
</view>
</view>
</span>
</view>
</view>
<view class="user">
<view class="title">
上传材料
</view>
<view class="item">
<span>身份证正面</span>
<span class='addition'>
<u-upload :action="action" :form-data="{
type:'cardFrontUrl'
}" :header="header" :file-list="fileList" :max-count="1" @on-uploaded="uploadedcardFront"></u-upload>
</span>
</view>
<view class="item">
<span>身份证反面</span>
<span class='addition'>
<u-upload :action="action" :form-data="{
type:'cardBackUrl'
}" :header="header" :file-list="fileList" :max-count="1" @on-uploaded="uploadedcardBack"></u-upload>
</span>
</view>
<view class="item" style="line-height: 50rpx;">
<span>近期白底免冠证件照(1,JPG格式,&lt;20KB)</span>
<span class='addition'>
<u-upload :action="action" :form-data="{
type:'photoUrl'
}" :header="header" :file-list="fileList" :max-count="1" @on-uploaded="uploadedphoto"></u-upload>
</span>
</view>
<view class="item" style="line-height: 50rpx;">
<span>学历证明(毕业证或学信网截图)</span>
<span class='addition'>
<u-upload :action="action" :form-data="{
type:'certificateUrl'
}" :header="header" :file-list="fileList" :max-count="1" @on-uploaded="uploadedcertificate"></u-upload>
</span>
</view>
</view>
<view class="user">
<view class="title">
声明与签字
</view>
<view style="line-height: 40rpx;font-size: 32rpx;color:red;padding:0 10rpx">
本人确认以上信息真实有效并同意考试机构核实相关信息
</view>
<view class="Package">
签名
<span style='color:#C5C3C3;padding-left: 10rpx;font-size: 30rpx;'></span>
<view class="checkboxs">
<view class="uppicture" @tap='signatureshow=true'>
<image v-if="!userSignaturePictureUrl" style="width: 36rpx;height: 36rpx;margin:8% 0 0 35%"
src="@/static/autograph.png" mode="">
</image>
<span v-if="!userSignaturePictureUrl">点此签名</span>
<image v-else :src="userSignaturePictureUrl" mode=""></image>
</view>
</view>
</view>
<view class="item">
<span>日期</span>
<span class='addition'>
{{personInfo.regTime}}
</span>
</view>
</view>
<view class="priceback">
<view class="queren" @tap='updata'>保存</view>
</view>
<u-toast ref="uToast" />
<u-mask :show="signatureshow" @click="signatureshow = false">
<view style="position:absolute;bottom:0;height:900rpx;width:100%;background-color: #fff;"
v-if='signatureshow'>
<signature @userSignaturePictureUrl='userSignaturePicture' @click.native.stop
style='background-color: #F4F5F7;width: 100%;height: 900rpx;'></signature>
</view>
</u-mask>
<u-select v-model="examshow" :list="examlist" @confirm="examconfirm" value-name="id"
label-name="title"></u-select>
<u-calendar v-model="trainDateshow" mode="range" @change="trainDatechange"></u-calendar>
<u-toast ref="uToast" />
</view>
</template>
<script>
import {
info
} from '@/api/user/index.js'
import {
getExamList,
save
} from '@/api/addexam/index.js'
import baseurl from '@/api/baseurl.js'
import signature from '@/components/signature/signature.vue'
export default {
components: {
signature
},
data() {
return {
examshow: false,
trainDateshow: false,
signatureshow: false,
userSignaturePictureUrl: null,
examlist: [],
educationlist: [{
name: '初中',
label: 1,
checked: false,
},
{
name: '高中/中专',
label: 2,
checked: false,
},
{
name: '大专及以上',
label: 3,
checked: false,
}
],
regTypelist: [{
name: '普通医疗护理员',
label: 1,
checked: false,
},
{
name: '老年医疗护理员',
label: 2,
checked: false,
},
{
name: '孕产妇和新生儿医疗护理员',
label: 3,
checked: false,
}
],
action: baseurl + '/exam/api/file/upload',
header: {
token: uni.getStorageSync('examh5token')
},
fileList: [],
personInfo: {
"address": "",
"cardBack": "",
"cardCopy": "", //
"cardFront": "",
"certificate": "",
"education": 0,
"email": "",
"examId": "",
startTime: undefined,
endTime: undefined,
startDate: undefined,
endDate: undefined,
"graduateSchool": "",
"major": "",
"phone": "",
"photo": "",
"physicalReport": "", //
"realName": "",
"regTime": "",
"regType": 0,
"signPicture": "",
"title": "",
"trainEndDate": "",
"trainInstitution": "",
"trainStartDate": "",
"userId": "",
"userName": "",
examFee: undefined,
},
}
},
onShow() {},
onLoad() {
this.getTodayDate()
this.myInfo()
},
methods: {
uploadedcertificate(res) {
this.personInfo.certificate = res[0].response.data.url
},
uploadedphoto(res) {
this.personInfo.photo = res[0].response.data.url
},
uploadedcardBack(res) {
this.personInfo.cardBack = res[0].response.data.url
},
uploadedcardFront(res) {
this.personInfo.cardFront = res[0].response.data.url
},
examconfirm(e) {
let obj = this.examlist.filter(el => el.id == e[0].value)
this.personInfo.title = e[0].label
this.personInfo.examId = e[0].value
this.personInfo.startTime = obj[0].startTime
this.personInfo.endTime = obj[0].endTime
this.personInfo.startDate = obj[0].startDate
this.personInfo.endDate = obj[0].endDate
this.personInfo.examFee = obj[0].examFee
},
trainDatechange(e) {
this.personInfo.trainEndDate = e.endDate
this.personInfo.trainStartDate = e.startDate
},
//
userSignaturePicture(data) {
let that = this
this.userSignaturePictureUrl = data
this.signatureshow = false
uni.uploadFile({
url: baseurl + '/exam/api/file/upload',
filePath: this.userSignaturePictureUrl,
name: 'file',
header: this.header,
formData: {
type: 'signPictureUrl'
},
timeout: 5000,
success(res) {
that.personInfo.signPicture = JSON.parse(res.data).data.url
}
})
},
//
updata() {
save(this.personInfo).then(res => {
if (res.code == 0) {
this.$refs.uToast.show({
title: '报名考试成功!',
type: 'success',
duration: '1500',
back: true
})
}
})
},
myInfo() {
info({
token: uni.getStorageSync('examh5token')
}).then(res => {
if (res.code == 0) {
uni.setStorageSync("examh5token", res.data.token)
uni.setStorageSync("examh5user", {
id: res.data.id,
phone: res.data.phone,
name: res.data.realName,
cardNo: res.data.userName
})
this.personInfo.realName = res.data.realName
this.personInfo.userId = res.data.id
this.personInfo.phone = res.data.phone
this.personInfo.userName = res.data.userName
this.personInfo.sex = this.getsex(res.data.userName)
this.personInfo.birthday = this.getbirthday(res.data.userName)
}
})
getExamList().then(res => {
this.examlist = res.data
})
},
getTodayDate() {
const today = new Date();
this.personInfo.regTime = today.toISOString().split('T')[0];
},
getsex(idCard) {
// Vue
if (!idCard || idCard.length !== 18) {
return '身份证号码不合法';
}
// 17
const genderChar = idCard.charAt(16);
//
if (isNaN(genderChar)) {
return '身份证号码不合法';
}
//
const genderNum = parseInt(genderChar, 10);
return genderNum % 2 === 1 ? '男' : '女';
},
getbirthday(idCard) {
// Vue
if (!idCard) {
return '身份证号码不能为空';
}
// 18
if (idCard.length === 18) {
if (!/^\d{17}[\dXx]$/.test(idCard)) {
return '身份证号码格式不正确';
}
// YYYYMMDD
const birthdayStr = idCard.substr(6, 8);
// YYYY-MM-DD
return `${birthdayStr.substr(0, 4)}-${birthdayStr.substr(4, 2)}-${birthdayStr.substr(6, 2)}`;
}
// 15
if (idCard.length === 15) {
if (!/^\d{15}$/.test(idCard)) {
return '身份证号码格式不正确';
}
// YYMMDD
const birthdayStr = idCard.substr(6, 6);
// 19YY-MM-DD (1519XX)
return `19${birthdayStr.substr(0, 2)}-${birthdayStr.substr(2, 2)}-${birthdayStr.substr(4, 2)}`;
}
return '身份证号码长度不正确';
},
argAdd(arg1, arg2) {
//
var _this = this,
r1 = 0,
r2 = 0,
m = 0;
try {
r1 = arg1.toString().split(".")[1].length
} catch (e) {}
try {
r2 = arg2.toString().split(".")[1].length
} catch (e) {}
m = Math.pow(10, Math.max(r1, r2))
return _this.argDiv((_this.argMul(arg1, m) + _this.argMul(arg2, m)), m)
},
argSubtr(arg1, arg2) {
//
var _this = this,
r1 = 0,
r2 = 0,
m = 0;
try {
r1 = arg1.toString().split(".")[1].length
} catch (e) {}
try {
r2 = arg2.toString().split(".")[1].length
} catch (e) {}
m = Math.pow(10, Math.max(r1, r2));
return _this.argDiv((_this.argMul(arg1, m) - _this.argMul(arg2, m)), m)
},
argMul(arg1, arg2) {
//
var _this = this,
m = 0,
s1 = arg1.toString(),
s2 = arg2.toString();
try {
m += s1.split(".")[1].length
} catch (e) {}
try {
m += s2.split(".")[1].length
} catch (e) {}
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m)
},
argDiv(arg1, arg2) {
//
var _this = this,
t1 = 0,
t2 = 0,
r1, r2;
try {
t1 = arg1.toString().split(".")[1].length
} catch (e) {}
try {
t2 = arg2.toString().split(".")[1].length
} catch (e) {}
r1 = Number(arg1.toString().replace(".", ""))
r2 = Number(arg2.toString().replace(".", ""))
return _this.argMul((r1 / r2), Math.pow(10, t2 - t1));
},
}
}
</script>
<style lang="scss">
@import './addexam.scss'
</style>

175
pages/examlist/examlist.vue Normal file
View File

@ -0,0 +1,175 @@
<template>
<view class="">
<u-navbar :is-back="false" title="考试列表" :background="background" title-color="white">
<view class="navbar-right" slot="right" @click="signup">
新增考试
</view>
</u-navbar>
<view class="app">
<view class="items" v-if="list.length>0">
<view class="item" v-for="(item,index) in list">
<view class="title">
<view class="text">
{{item.title}}
</view>
<view class="image">
<image src="../../static/jinbi.png" mode=""></image>
<span v-if="item.examFee&&item.examFee>=0">
{{item.examFee}}
</span>
<span v-else>
0
</span>
</view>
</view>
<view class="time" style="margin-top: 20rpx;color: #EA706A;font-weight:600">
考试费用{{item.examFee}}
</view>
<view class="time">
考试日期{{item.startDate}}{{item.endDate}}
</view>
<view class="time">
考试时间{{item.startTime}}{{item.endTime}}
</view>
<view class="time">
考试时长{{item.totalTime}}分钟
</view>
<view class="time">
试卷总分{{item.totalScore}}
</view>
<view class="time">
考试人员{{user.name}}
</view>
<view class="time">
身份证{{user.cardNo}}
</view>
<view class="time">
手机号{{user.phone}}
</view>
</view>
</view>
<view v-else class="" style="margin-top: 100rpx;">
<u-empty text="暂无工单" mode="list" icon-size='240' font-size='32'></u-empty>
</view>
<u-toast ref="uToast" />
</view>
</view>
</template>
<script>
import {
getRegExamList
} from '@/api/examlist/index.js'
export default {
data() {
return {
user: uni.getStorageSync('examh5user'),
background: {
backgroundColor: "#4C7BC9",
},
list: [], //list
total: 0,
}
},
methods: {
//
signup() {
uni.navigateTo({
url: "/pages/addexam/addexam"
})
},
//list
info() {
getRegExamList().then(res => {
this.list = res.data
})
},
},
onLoad() {},
onShow() {
this.info();
},
onReachBottom() { //
if (this.list.length >= this.total) {} else {}
},
onPullDownRefresh() { //
this.info();
setTimeout(() => {
uni.stopPullDownRefresh();
}, 1000);
},
}
</script>
<style lang="scss">
.navbar-right {
color: #fff;
padding-right: 20rpx;
}
.app {
padding: 0;
.items {
width: 96%;
margin: 20rpx auto;
.item {
margin: 10rpx auto;
background-color: #fff;
box-shadow: 0rpx 9rpx 31rpx 9rpx rgba(0, 0, 0, 0.03);
border-radius: 5rpx;
padding: 10rpx;
width: 100%;
position: relative;
color: #333333;
font-size: 30rpx;
padding-bottom: 60rpx;
.time {
font-size: 30rpx;
}
.title {
line-height: 100rpx;
border-bottom: 1rpx solid #E6E6E6;
font-size: 38rpx;
color: #333333;
position: relative;
height: 100rpx;
.text {
height: 100%;
overflow: hidden; //
display: -webkit-box;
-webkit-line-clamp: 1; //
-webkit-box-orient: vertical;
width: calc(100% - 170rpx);
}
.image {
position: absolute;
right: 0%;
top: 50%;
transform: translateY(-50%);
display: inline-block;
font-size: 30rpx;
color: #EA706A;
span {
display: inline-block;
width: 100rpx;
}
image {
transform: translateY(20%);
width: 40rpx;
height: 40rpx;
padding-right: 10rpx;
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,74 @@
.app {
height: calc(100vh - 44px);
padding: 20rpx 0;
.item {
font-size: 34rpx;
margin: 0 auto 20rpx;
width: 94%;
height: 100rpx;
line-height: 100rpx;
background-color: #fff;
box-shadow: 0px 9rpx 31rpx 9rpx rgba(0, 0, 0, 0.03);
border-radius: 20rpx;
position: relative;
.lefttext {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 3%;
}
.righttext {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 18%;
height: 100rpx;
}
.lefttext,
.righttext {
::v-deep .uni-input-input {
font-size: 34rpx;
}
::v-deep .uni-input-wrapper {
height: 100rpx;
line-height: 100rpx;
}
::v-deep .uni-input-placeholder {
line-height: 100rpx;
font-size: 34rpx;
font-weight: 400;
color: #C3C1C1;
}
}
.obtaincode {
text-align: center;
font-size: 36rpx;
color: #4C7BC9;
line-height: 100rpx;
position: absolute;
right: 5%;
top: 0%;
}
}
.loginbtn {
width: 80%;
height: 100rpx;
text-align: center;
line-height: 100rpx;
background: #4C7BC9;
border-radius: 51rpx;
font-size: 41rpx;
color: #FFFFFF;
position: absolute;
top: 60%;
left: 10%;
}
}

View File

@ -0,0 +1,158 @@
<template>
<view class="app">
<view class="item">
<view class="lefttext">
姓名
</view>
<u-input class="righttext" style='left:23%' type="text" placeholder="请输入" maxlength="11"
v-model="phonenumber" />
</view>
<view class="item">
<view class="lefttext">
身份证号
</view>
<u-input class="righttext" style='left:30%' type="text" placeholder="请输入" maxlength="11"
v-model="phonenumber" />
</view>
<view class="item">
<view class="lefttext">
手机号
</view>
<u-input class="righttext" style='left:23%' type="text" placeholder="请输入" maxlength="11"
v-model="phonenumber" />
</view>
<view class="item">
<view class="lefttext">
输入新密码
</view>
<u-input class='righttext' style='left:30%' placeholder="请输入密码" maxlength="10" type="password"
:border="false" :password-icon="true" v-model="newpassword" />
</view>
<view class="item">
<view class="lefttext">
重复新密码
</view>
<u-input class='righttext' style='left:30%' placeholder="请再次输入密码" maxlength="10" type="password"
:border="false" :password-icon="true" v-model="password" />
</view>
<!-- <view class="item">
<view class="lefttext">
验证码
</view>
<input class="righttext" style='left:23%' type="text" placeholder="" maxlength="6" v-model="verification" />
<view class="obtaincode" :style="{'color':getCodeBtnColor}" @click.stop="getCode()">
{{getCodeText}}
</view>
</view> -->
<view class="loginbtn" @tap='pwdlogin'>
确定
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
// import {
// updatePassword
// } from '@/api/forgotPassword/forgotPassword.js'
export default {
data() {
return {
phonenumber: '',
password: '',
newpassword: '',
getCodeText: '获取验证码', //
getCodeBtnColor: "#4C7BC9", //color
timer: null,
}
},
onLoad(options) {
if (options.phonenumber) {
this.phonenumber = options.phonenumber
}
},
methods: {
pwdlogin() {
var that = this
if (this.password !== this.newpassword) {
this.$refs.uToast.show({
title: '密码输入不一致,请重新输入',
type: 'error',
duration: '1500'
})
} else {
updatePassword(this.phonenumber, this.password, this.verification).then(res => {
if (res.code == 200) {
this.$refs.uToast.show({
title: '密码修改成功',
type: 'success',
duration: '1500'
})
if (that.timer) {
clearTimeout(that.timer)
}
that.timer = setTimeout(e => {
uni.navigateTo({
url: `/pages/login/login?phonenumber=${this.phonenumber}&password=${this.password}`
})
}, 1500)
} else {
this.$refs.uToast.show({
title: res.msg,
type: 'error'
})
}
})
}
},
//
getCode() {
uni.hideKeyboard() //
if (this.getCodeisWaiting) { //
return;
}
if (!(/^1(3|4|5|6|7|8|9)\d{9}$/.test(this.phonenumber))) { //
uni.showToast({
title: '请填写正确手机号码',
icon: "none"
});
return false;
}
this.getCodeText = "发送中..." //
this.getCodeisWaiting = true;
this.getCodeBtnColor = "rgba(138,139,133,1)" //
//
//setTimeout(()
setTimeout(() => {
uni.showToast({
title: '验证码已发送',
icon: "none"
}); //
// this.code = '1234'; // 1234
this.setTimer(); //
}, 1000)
},
// setTimer 使SetTimer
setTimer() {
let holdTime = 60; //
this.getCodeText = "重新获取(60)"
//setInterval
//setInterval clearInterval
this.Timer = setInterval(() => {
if (holdTime <= 0) {
this.getCodeisWaiting = false;
this.getCodeBtnColor = "#4C7BC9";
this.getCodeText = "获取验证码"
clearInterval(this.Timer); //
return; //
}
this.getCodeText = "重新获取(" + holdTime + ")"
holdTime--;
}, 1000)
},
},
}
</script>
<style lang="scss">
@import "./forgotPassword.scss";
</style>

89
pages/login/login.scss Normal file
View File

@ -0,0 +1,89 @@
.app {
font-family: DengXian;
color: #C3C1C1;
font-weight: 400;
height:calc(100vh - 44px);
.switch {
text-align: center;
height: 100rpx;
color: #46ABD7;
position: absolute;
top: 70%;
}
.loginbtn {
width: 80%;
height: 100rpx;
text-align: center;
line-height: 100rpx;
background: #4C7BC9;
border-radius: 51rpx;
font-size: 41rpx;
color: #FFFFFF;
position: absolute;
top: 57%;
left: 10%;
}
.loginphone {
background-color: #fff;
width: 80%;
height: 120rpx;
position: absolute;
left: 10%;
top: 23%;
input {
font-size: 45rpx;
color: #000000;
}
::v-deep .uni-input-placeholder {
padding-left: 40rpx;
font-size: 41rpx;
font-weight: 400;
color: #C3C1C1;
}
.phone {
padding-left: 40rpx;
height: 100%;
box-shadow: 0px 9px 31px 9px rgba(0, 0, 0, 0.03);
}
}
.logincode {
position: absolute;
left: 10%;
.obtaincode {
text-align: center;
font-size: 37rpx;
color: #4C7BC9;
line-height: 162rpx;
position: absolute;
left: 65%;
top: 0%;
}
::v-deep .u-input__input {
padding-left: 20rpx;
color: #000000;
font-size: 42rpx;
font-weight: 400;
height: 100%;
}
}
.title {
height: 50rpx;
font-size: 52rpx;
font-weight: bold;
color: #000000;
line-height: 44rpx;
position: absolute;
top: 13%;
left: 13%;
}
}

116
pages/login/login.vue Normal file
View File

@ -0,0 +1,116 @@
<template>
<view class="app">
<view class="title">
账号登录
</view>
<view class="loginphone logincode">
<u-input class="code phone" type="text" placeholder="身份证号" maxlength="18" v-model="phone" />
</view>
<view class="logincode loginphone" style="top: 40%;">
<u-input class='code phone' placeholder="密码" maxlength="20" v-model="password" type="password"
:password-icon="true" />
</view>
<view class="loginbtn" @tap='pwdlogin'>
登录
</view>
<!-- <view class="switch" @tap='goforgotPassword' style="left:10%">
忘记密码
</view> -->
<view class="switch" @tap='goregister' style="right:10%">
没有账号,请先注册
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
import {
info
} from '@/api/user/index.js'
import {
login
} from '../../api/login/index.js'
export default {
data() {
return {
phone: '',
password: '',
};
},
//
onLoad(options) {
if (options.phone && options.password) {
this.phone = options.phone
this.password = options.password
}
},
onShow() {
if (uni.getStorageSync('examh5token')) {
uni.switchTab({
url: "/pages/examlist/examlist"
})
// info({
// token: uni.getStorageSync('examh5token')
// }).then(res => {
// if (res.code == 0) {
// uni.setStorageSync("examh5token", res.data.token)
// uni.setStorageSync("examh5user", {
// id: res.data.id,
// phone: res.data.phone,
// name: res.data.realName,
// cardNo: res.data.userName
// })
// }
// })
}
},
methods: {
pwdlogin() {
login({
username: this.phone,
password: this.password
}).then(res => {
if (res.code == 0) {
uni.setStorageSync("examh5token", res.data.token)
uni.setStorageSync("examh5user", {
id: res.data.id,
phone: res.data.phone,
name: res.data.realName,
cardNo: res.data.userName
})
this.$refs.uToast.show({
title: "登录成功!",
type: 'success',
duration: '1500'
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/examlist/examlist',
})
}, 1500);
} else {
this.$refs.uToast.show({
title: res.msg,
type: 'error'
})
}
})
},
//
goregister() {
uni.navigateTo({
url: '/pages/register/register'
})
},
//
goforgotPassword() {
uni.navigateTo({
url: `/pages/forgotPassword/forgotPassword?phone=${this.phone}`
})
},
},
}
</script>
<style lang="scss">
@import "./login.scss";
</style>

View File

@ -0,0 +1,162 @@
.app {
height:calc(100vh - 44px);
padding:20rpx 0;
.Agreement{
width: 100%;
background-color: #F4F5F7;
text-align: center;
height: 1000rpx;
position: absolute;
top:5%;
font-size: 30rpx;
.title{
height: 100rpx;
line-height: 100rpx;
border-bottom: 1px solid #eeeeee;
font-size: 34rpx;
margin: 0px auto;
width: 100%;
text-align: center;
}
.scroll-Y{
height:830rpx ;
overflow-y:scroll;
text-align: left;
text-indent: 2em;
}
.cancel {
height:70rpx;
line-height: 70rpx;
font-size: 32rpx;
background-color: #F4F5F7;
position: absolute;
border-top: 1rpx solid #000000;
bottom:0;
right:0;
width: 50%;
color: #000000;
}
.determine {
height:70rpx;
line-height: 70rpx;
font-size: 32rpx;
width: 50%;
color: #F4F5F7;
background: #4C7BC9;
position: absolute;
bottom:0;
left:0;
}
}
.item{
font-size: 34rpx;
margin: 0 auto 20rpx;
width: 94%;
height:100rpx;
line-height: 100rpx;
background-color: #fff;
box-shadow: 0px 9rpx 31rpx 9rpx rgba(0,0,0,0.03);
border-radius: 20rpx;
position: relative;
.lefttext{
position: absolute;
top:50%;
transform: translateY(-50%);
left:3%;
}
.righttext{
position: absolute;
top:50%;
transform: translateY(-50%);
width: 68%;
left:18%;
height:100rpx;
}
.lefttext,.righttext{
::v-deep .uni-input-input{
font-size: 34rpx;
}
::v-deep .uni-input-wrapper{
height:100rpx;
line-height: 100rpx;
}
::v-deep .uni-input-placeholder {
line-height: 100rpx;
font-size: 34rpx;
font-weight: 400;
color: #C3C1C1;
}
}
.obtaincode {
text-align: center;
font-size: 36rpx;
color: #4C7BC9;
line-height: 100rpx;
position: absolute;
right:5%;
top: 0%;
}
}
.radio-content {
margin: 50rpx auto;
width: 70%;
text-align: center;
font-size: 28rpx;
position: relative;
.agreement {
position: absolute;
top:50%;
left:20%;
transform: translateY(-50%);
color: #878987;
}
.radio-right {
height: 100rpx;
.radio {
display: inline-block;
width: 35rpx;
height: 35rpx;
border-radius: 70%;
border: 2rpx solid #178ffb;
position: absolute;
top:50%;
left:5%;
transform: translateY(-50%);
.radio-active {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background-color: #178ffb;
position: absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%);
}
}
}
}
.switch {
text-align: center;
line-height: 60rpx;
color: #46ABD7;
position: absolute;
top: 73%;
}
.loginbtn {
width: 70%;
height: 100rpx;
text-align: center;
line-height: 100rpx;
background: #4C7BC9;
border-radius: 51rpx;
font-size: 41rpx;
color: #FFFFFF;
position: absolute;
top: 62%;
left: 15%;
}
}

247
pages/register/register.vue Normal file
View File

@ -0,0 +1,247 @@
<template>
<view class="app">
<view class="item">
<view class="lefttext">
姓名
</view>
<u-input class="righttext" style="width: 80%;" type="text" placeholder="请输入" maxlength="10"
v-model="realName" />
</view>
<view class="item">
<view class="lefttext">
身份证号
</view>
<u-input class="righttext" style='left:30%' type="text" placeholder="请输入" maxlength="18"
v-model="patientName" />
</view>
<view class="item">
<view class="lefttext">
手机号
</view>
<u-input class="righttext" style='left:23%;width: 77%;' type="text" placeholder="请输入" maxlength="11"
v-model="phone" />
</view>
<view class="item">
<view class="lefttext">
输入密码
</view>
<u-input class='righttext' style='left:30%' placeholder="请输入密码" maxlength="20" v-model="password"
type="password" :border="false" :password-icon="true" />
</view>
<view class="item">
<view class="lefttext">
重复密码
</view>
<u-input class='righttext' style='left:30%' placeholder="请再次输入密码" maxlength="20" v-model="newpassword"
type="password" :border="false" :password-icon="true" />
</view>
<!-- <view class="item">
<view class="lefttext">
验证码
</view>
<input class="righttext" style='left:23%' type="text" placeholder="" maxlength="6" v-model="verification" />
<view class="obtaincode" :style="{'color':getCodeBtnColor}" @click.stop="getCode()">
{{getCodeText}}
</view>
</view> -->
<view class="loginbtn" @tap='register'>
注册
</view>
<view class="switch" style="right:5%" @tap='gologin'>
已有账号,去登录
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
import {
reg
} from '@/api/register/index.js'
export default {
components: {},
data() {
return {
patientName: '',
realName: "",
phone: '',
password: '',
newpassword: '',
verification: '',
getCodeText: '获取验证码', //
getCodeBtnColor: "#4C7BC9", //color
getCodeisWaiting: false, //
};
},
methods: {
//
register() {
if (this.password !== this.newpassword) {
this.$refs.uToast.show({
title: '密码输入不一致,请重新输入',
type: 'error',
duration: '1500'
})
} else {
reg({
password: this.password,
realName: this.realName,
userName: this.patientName,
phone: this.phone
}).then(res => {
if (res.code == 0) {
uni.setStorageSync("examh5token", res.data.token)
uni.setStorageSync("examh5user", {
id: res.data.id,
phone: res.data.phone,
name: res.data.realName,
cardNo: res.data.userName
})
this.$refs.uToast.show({
title: '注册成功,前往登录',
type: 'success',
duration: '1500'
})
setTimeout(e => {
uni.reLaunch({
url: `/pages/login/login?phone=${this.patientName}&password=${this.password}`
})
}, 1500)
} else {
this.$refs.uToast.show({
title: res.msg,
type: 'error'
})
}
})
}
},
//
gologin() {
uni.reLaunch({
url: `/pages/login/login`
})
},
//
getCode() {
uni.hideKeyboard() //
if (this.getCodeisWaiting) { //
return;
}
if (!(/^1(3|4|5|6|7|8|9)\d{9}$/.test(this.phone))) { //
uni.showToast({
title: '请填写正确手机号码',
icon: "none"
});
return false;
}
this.getCodeText = "发送中..." //
this.getCodeisWaiting = true;
this.getCodeBtnColor = "rgba(138,139,133,1)" //
//
//setTimeout(()
setTimeout(() => {
uni.showToast({
title: '验证码已发送',
icon: "none"
}); //
// this.code = '1234'; // 1234
this.setTimer(); //
}, 1000)
},
// setTimer 使SetTimer
setTimer() {
let holdTime = 60; //
this.getCodeText = "重新获取(60)"
//setInterval
//setInterval clearInterval
this.Timer = setInterval(() => {
if (holdTime <= 0) {
this.getCodeisWaiting = false;
this.getCodeBtnColor = "#4C7BC9";
this.getCodeText = "获取验证码"
clearInterval(this.Timer); //
return; //
}
this.getCodeText = "重新获取(" + holdTime + ")"
holdTime--;
}, 1000)
},
doReg() {
// uni.hideKeyboard() //
// //
// if (!(/^1(3|4|5|6|7|8|9)\d{9}$/.test(this.phoneNumber))) { //
// uni.showToast({
// title: '',
// icon: "none"
// });
// return false;
// }
// //使
// if (this.code != 1234) {
// uni.showToast({
// title: '',
// icon: "none"
// });
// return false;
// }
// uni.showLoading({
// title: '...'
// })
// //使
// setTimeout(() => {
// uni.getStorage({
// key: 'UserList',
// success: (res) => {
// //md5
// res.data.push({
// username: this.phoneNumber,
// passwd: md5(this.passwd)
// })
// uni.setStorage({
// key: 'UserList',
// data: res.data,
// success: function() {
// uni.hideLoading()
// uni.showToast({
// title: '',
// icon: "success"
// });
// setTimeout(function() {
// uni.navigateBack();
// }, 1000)
// }
// });
// },
// fail: (e) => {
// uni.hideLoading()
// //UserList
// uni.setStorage({
// key: 'UserList',
// data: [{
// username: this.phoneNumber,
// passwd: md5(this.passwd)
// }],
// success: function() {
// uni.hideLoading()
// uni.showToast({
// title: '',
// icon: "success"
// });
// setTimeout(function() {
// uni.navigateBack();
// }, 1000)
// },
// fail: function(e) {
// }
// });
// }
// });
// }, 1000)
},
},
}
</script>
<style lang="scss">
@import "./register.scss";
</style>

240
pages/user/user.vue Normal file
View File

@ -0,0 +1,240 @@
<template>
<view class="app">
<image class="circular" src="../../static/homepage.png" mode=""></image>
<view class="user" v-if="Personallist">
<image class="img" src="../../static/user2.png" mode=""></image>
<view class="phone" v-if="Personallist.realName">
{{Personallist.realName}}
</view>
<view class="nickname" v-if="Personallist.userName">
{{Personallist.userName}}
</view>
</view>
<view class="user" v-else>
<image class="img" src="../../static/user2.png" mode=""></image>
<view class="login" @tap='gologin'>
登录
</view>
</view>
<view class="bottomitems">
<view class="bottomitem" @tap='goremove'>
<image src="../../static/shezhi.png" mode=""></image>
<view class="">
退出账号
</view>
</view>
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
import {
info
} from '@/api/user/index.js'
export default {
data() {
return {
Personallist: undefined,
}
},
onLoad() {
if (uni.getStorageSync('examh5token')) {
this.myInfo()
}
},
methods: {
myInfo() {
info({
token: uni.getStorageSync('examh5token')
}).then(res => {
if (res.code == 0) {
uni.setStorageSync("examh5token", res.data.token)
uni.setStorageSync("examh5user", {
id: res.data.id,
phone: res.data.phone,
name: res.data.realName,
cardNo: res.data.userName
})
this.Personallist = res.data
}
})
},
//
gologin() {
uni.reLaunch({
url: '/pages/login/login'
})
},
//退
goremove() {
let that = this
const value = uni.getStorageSync('examh5token');
if (value) {
uni.showModal({
title: '提示',
content: '确认要退出此账号吗',
success: function(res) {
if (res.confirm) {
uni.clearStorageSync();
that.Personallist = undefined
that.$refs.uToast.show({
title: '退出账号成功!',
type: 'success',
duration: '1000'
})
if (that.timer) {
clearTimeout(that.timer)
}
that.timer = setTimeout(e => {
that.gologin();
}, 1000)
}
}
});
} else {
that.$refs.uToast.show({
title: '您未登录',
type: 'error',
duration: '1000'
})
}
},
}
}
</script>
<style lang="scss">
page {
background-color: #fff;
}
.app {
padding: 0;
font-size: 32rpx;
height: calc(100vh - 100rpx);
.bottomitems {
position: absolute;
bottom: 50rpx;
width: 100%;
.bottomitem {
width: 85%;
margin: 0 auto;
height: 120rpx;
position: relative;
border-bottom: 1rpx solid #F3F3F3;
view {
display: inline-block;
font-size: 35rpx;
color: #333333;
line-height: 120rpx;
margin-left: 80rpx;
}
image {
position: absolute;
top: 50%;
transform: translateY(-50%);
display: inline-block;
width: 50rpx;
height: 50rpx;
}
}
}
.topitems {
z-index: 999;
padding: 45rpx 0 35rpx;
width: 94%;
margin: 0 auto;
display: flex;
background: #FFFFFF;
box-shadow: 0rpx 3rpx 18rpx 0rpx rgba(79, 108, 254, 0.43);
border-radius: 5rpx;
justify-content: space-around;
text-align: center;
position: relative;
.topitem {
view {
font-size: 32rpx;
margin-top: 10rpx;
}
image {
width: 90rpx;
height: 90rpx;
}
}
}
.user {
width: 100%;
height: 500rpx;
position: relative;
color: #FFFFFF;
font-size: 38rpx;
z-index: 999;
.modify {
position: absolute;
right: 3%;
top: 250rpx;
font-size: 28rpx;
image {
width: 23rpx;
height: 23rpx;
padding-left: 10rpx;
}
}
.login {
text-align: center;
position: absolute;
top: 220rpx;
left: 35%;
font-size: 36rpx;
width: 180rpx;
line-height: 70rpx;
border: 1rpx solid #fff;
height: 70rpx;
border-radius: 20rpx;
}
.nickname {
position: absolute;
top: 260rpx;
left: 35%;
font-size: 28rpx;
}
.phone {
position: absolute;
top: 180rpx;
left: 35%;
}
.img {
width: 160rpx;
height: 160rpx;
border-radius: 50%;
background: #F6F6F6;
position: absolute;
top: 170rpx;
left: 8%;
// border: 4rpx solid #6DD8FC;
}
}
.circular {
z-index: 1;
width: 100%;
height: 500rpx;
position: absolute;
top: 0;
}
}
</style>

BIN
static/autograph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
static/homepage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
static/homepagew.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
static/homepagews.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
static/jinbi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
static/shezhi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
static/user2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
static/users.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
static/userw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

13
uni.promisify.adaptor.js Normal file
View File

@ -0,0 +1,13 @@
uni.addInterceptor({
returnValue (res) {
if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
return res;
}
return new Promise((resolve, reject) => {
res.then((res) => {
if (!res) return resolve(res)
return res[0] ? reject(res[0]) : resolve(res[1])
});
});
},
});

77
uni.scss Normal file
View File

@ -0,0 +1,77 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量同时无需 import 这个文件
*/
/* uni.scss */
@import 'uview-ui/theme.scss';
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f8f8f8;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:12px;
$uni-font-size-base:14px;
$uni-font-size-lg:16px;
/* 图片尺寸 */
$uni-img-size-sm:20px;
$uni-img-size-base:26px;
$uni-img-size-lg:40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:20px;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;