master
LiuJiaNan 2024-12-13 08:38:32 +08:00
commit 0ecbffa3cb
79 changed files with 8072 additions and 0 deletions

5
.eslintignore Normal file
View File

@ -0,0 +1,5 @@
public
dist
package.json
!.prettierrc.cjs
env.d.ts

74
.eslintrc.cjs Normal file
View File

@ -0,0 +1,74 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
"plugin:vue/vue3-recommended",
"standard",
"@vue/prettier",
"eslint:recommended",
],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
ignorePatterns: [
"uni_modules/**/*",
"hybrid/**/*",
"static/**/*",
"components/unitv-*/*",
],
plugins: ["vue"],
rules: {
"no-console": "warn",
"vue/multi-word-component-names": "off",
"vue/no-v-html": "off",
"vue/require-default-prop": "off",
camelcase: "off",
eqeqeq: "error",
"vue/no-template-shadow": "error",
"vue/attribute-hyphenation": "error",
"vue/html-end-tags": "error",
"vue/eqeqeq": "error",
"vue/component-name-in-template-casing": ["error", "kebab-case"],
"vue/enforce-style-attribute": [
"error",
{ allow: ["scoped", "module", "plain"] },
],
"vue/v-on-event-hyphenation": [
"error",
"always",
{
autofix: true,
},
],
"vue/require-explicit-emits": "error",
"no-unused-vars": [
"error",
{ vars: "all", args: "after-used", ignoreRestSiblings: false },
],
"linebreak-style": ["off", "windows"],
"no-restricted-properties": [
"error",
{ object: "Object", property: "assign" },
],
"no-restricted-syntax": [
"error",
{
selector: "VariableDeclarator[id.name='pd']",
message: "不允许使用 pd请改用有语义化的变量名",
},
{
selector: "ObjectExpression > Property[key.name='pd']",
message: "不允许使用 pd请改用有语义化的变量名",
},
],
},
globals: {
plus: "readonly",
uni: "readonly",
JSEncrypt: "readonly",
},
};

26
.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
unpackage
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.hbuilderx
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

4
.prettierrc.cjs Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
extends: ["@vue/prettier", "plugin:prettier/recommended"],
endOfLine: "crlf",
};

24
App.vue Normal file
View File

@ -0,0 +1,24 @@
<script>
export default {
onLaunch: function () {
console.log("App Launch");
// #ifdef APP-PLUS
plus.screen.lockOrientation("landscape-primary"); //
plus.navigator.setFullscreen(true); // (:)
// #endif
},
onShow: function () {
console.log("App Show");
},
onHide: function () {
console.log("App Hide");
},
};
</script>
<style lang="scss">
@import "styles/common.scss";
//::-webkit-scrollbar {
// display: none;
//}
</style>

1
api/index.js Normal file
View File

@ -0,0 +1 @@
// import { post } from "@/utils/request.js";

View File

@ -0,0 +1,150 @@
<template>
<view class="header">
<view v-if="isShowUserInfo" class="left">
<unitv-zone
id="header_zone"
class="header_zone"
:down="down"
:values="[0, 1]"
:column="2"
>
<unitv-item
:item="0"
class="item"
@click="fnNavigate('/pages/login/index')"
>
<view class="login_btn">登录</view>
</unitv-item>
<unitv-item
:item="1"
class="item user"
@click="fnNavigate('/pages/mine/index')"
>
<view class="avatar">
<image src="/static/avatar.png" />
</view>
<view>呀哈哈</view>
</unitv-item>
</unitv-zone>
</view>
<view class="right">
<view>{{ time }}</view>
<view class="logo">
<image src="/static/logo.png" />
</view>
</view>
</view>
</template>
<script>
import dayjs from "dayjs";
export default {
props: {
isShowUserInfo: {
type: Boolean,
default: true,
},
down: {
type: String,
default: "tabs_zone",
},
},
data() {
return {
time: "",
};
},
created() {
this.time = dayjs().format("HH:mm");
setInterval(() => {
this.time = dayjs().format("HH:mm");
}, 1000);
},
methods: {
fnNavigate(url) {
uni.navigateTo({
url,
});
},
},
};
</script>
<style scoped lang="scss">
.header {
width: 100%;
color: rgba(255, 255, 255, 0.5);
position: relative;
z-index: 99;
overflow: auto;
.left {
float: left;
.header_zone {
display: flex;
.item {
display: flex;
border-radius: 34rpx;
background-color: rgba(255, 255, 255, 0.1);
font-size: 12rpx;
margin-left: 10rpx;
.login_btn {
padding: 3rpx 15rpx;
border-radius: 34rpx;
}
&:first-child {
margin-left: 0;
}
&.user {
position: relative;
border-radius: 11rpx;
background-color: rgba(255, 255, 255, 0.1);
padding: 3rpx 10rpx 3rpx 30rpx;
.avatar {
position: absolute;
left: 0;
top: 0;
border: 2rpx solid #070a26;
border-radius: 50%;
font-size: 0;
image {
width: 19rpx;
height: 19rpx;
border-radius: 50%;
}
}
}
&.hover {
background-color: #254eff;
color: #fff;
}
}
}
}
.right {
float: right;
font-size: 12rpx;
display: flex;
align-items: center;
justify-content: end;
.logo {
margin-left: 26rpx;
image {
width: 62rpx;
height: 12rpx;
}
}
}
}
</style>

View File

@ -0,0 +1,33 @@
<template>
<view class="loading">
<image
src="/static/loading1000.gif"
mode="aspectFit"
:style="{ width, height }"
/>
</view>
</template>
<script>
export default {
props: {
width: {
type: String,
default: "100vw",
},
height: {
type: String,
default: "100vh",
},
},
};
</script>
<style scoped lang="scss">
.loading {
position: fixed;
z-index: 10;
inset: 0;
background-color: #1d1c37;
}
</style>

View File

@ -0,0 +1,116 @@
<template>
<view :id="id" :class="`${selected?selectClass:''} ${hovered?hoverClass:''}`">
<slot></slot>
</view>
</template>
<script>
import {
mapActions,
mapState
} from 'vuex';
export default {
name: "unitvPage",
inject: ['pageId', 'zoneId', 'zoneState', 'zoneItems'],
props: {
item: {
type: Number
},
hoverClass: {
type: String,
default: 'hover'
},
selectClass: {
type: String,
default: 'active'
},
isSelctByClick: {
type: Boolean,
default: false
},
isSelect: {
type: Boolean,
default: false
}
},
updated() {
this.zoneItems[this.item] = this;
},
created() {
this.zoneItems[this.item] = this;
this.selected=this.isSelect;
},
mounted() {
if (this.zoneState.curZone && this.zoneState.curItem == this.item) {
this.handleHover();
this.refreshScroll();
this.refreshState();
}
},
data() {
return {
selected: false,
hovered: false
}
},
computed: {
...mapState(['currentZone', 'currentPage']),
id: function() {
return this.zoneId + '_' + this.item;
}
},
methods: {
clientRect(callback) {
let view = uni.createSelectorQuery().in(this).select("#" + this.id);
view.boundingClientRect(data => {
callback(data);
}).exec();
},
refreshScroll() {
this.clientRect((clientRect) => {
this.currentPage.RefreshScrollTop(clientRect);
if (this.zoneState.curScroll != null) {
this.zoneState.curScroll.RefreshScrollLeft(clientRect);
}
});
},
refreshState() {
if (!this.isSelctByClick) {
if (!this.zoneState.curZone && this.zoneState.curItem == this.item) {
this.selected = true;
} else {
this.selected = false;
}
}
if (this.zoneState.curZone && this.zoneState.curItem == this.item) {
this.hovered = true;
} else {
this.hovered = false;
}
},
handleHover() {
this.$emit("hover", this.item);
},
handleClick() {
if (this.isSelctByClick) {
this.zoneItems.forEach(item => {
item.selected = false
})
this.selected = true
}
this.$emit("click", this.item);
}
},
watch: {
hovered: function(newValue) {
if (newValue != "") {
this.handleHover();
this.refreshScroll();
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,222 @@
<template>
<scroll-view v-show="current" scroll-y="true" :style="{height:windowHeight+'px'}" ref="page" :scroll-top="scrollTop"
@scroll="scroll">
<view @click="whole.onClick" id="onClick"></view>
<slot></slot>
</scroll-view>
<!-- <unitv-transition :duration="500" :mode-class="['fade']" :styles="transfromClass" :show="current">
</unitv-transition> -->
</template>
<script>
import {
mapMutations,
mapState
} from 'vuex';
export default {
name: "unitvPage",
props: {
id: {
type: String,
required: true
},
prePageId: {
type: String
},
show: {
type: Boolean,
default: false
},
handleEvent: {
type: Boolean,
default: false
}
},
provide() {
return {
pageId: this.id,
pageState: this.pageState
}
},
created() {
if (this.show) {
this.switchPage(this);
}
uni.getSystemInfo({
success: (res) => {
this.windowHeight = res.windowHeight;
}
});
},
mounted() {
this.pushPage(this);
},
computed: {
...mapState(['currentZone', 'currentPage', 'allPages']),
current: function() {
return this.currentPage == this;
}
},
data() {
return {
key: '',
windowHeight: null,
scrollTop: 0,
old: {
scrollTop: 0
},
Zone: [],
pageState: {
curZoneId: "",
handleEvent: this.handleEvent
},
transfromClass: {
'position': 'fixed',
'bottom': 0,
'top': 0,
'left': 0,
'right': 0,
/* #ifndef APP-NVUE */
'display': 'flex',
/* #endif */
'justify-content': 'center',
'align-items': 'center'
}
}
},
methods: {
...mapMutations(['switchPage', 'switchZone', 'pushPage']),
showPage() {
this.switchPage(this);
this.switchZone(this.Zone[this.pageState.curZoneId]);
this.Zone[this.pageState.curZoneId].refreshItem();
return true;
},
pushZone(zone) {
this.Zone[zone.id] = zone;
},
keyCodeClick(keyCode) {
this.key = keyCode;
uni.$emit("keyDown", keyCode);
switch (keyCode) {
case 'KeyUp':
this.evtArrow('up');
break;
case 'KeyDown':
this.evtArrow('down');
break;
case 'KeyLeft':
this.evtArrow('left');
break;
case 'KeyRight':
this.evtArrow('right');
break;
case 'KeyEnter':
this.evtEnter();
break;
case 'KeyBack':
this.evtBack();
break;
};
},
evtArrow: function(Arrow) {
var zone = this.currentZone;
zone.evtArrow(Arrow);
},
evtEnter: function() {
this.currentZone.evtEnter();
},
evtBack: function() {
this.$emit("back");
this.key = "KeyBack";
},
scroll: function(e) {
this.old.scrollTop = e.detail.scrollTop
},
ChangeZone(zoneId) {
var zone = this.Zone[zoneId];
if (zone) {
this.currentZone.zoneState.curZone = false;
this.pageState.curZoneId = zoneId;
zone.zoneState.curZone = true;
this.switchZone(this.Zone[zoneId]);
this.Zone[zoneId].refreshItem();
}
},
RefreshScrollTop(clientRect) {
var top = clientRect.top;
if (top > 0) {
top = clientRect.bottom;
}
if (top > this.windowHeight) {
var top1 = this.old.scrollTop + (top - this.windowHeight + 10);
this.scrollTop = top1
} else if (top < 0) {
this.scrollTop = this.old.scrollTop + (top - 20);
}
}
},
watch: {
handleEvent: function(val) {
this.pageState.handleEvent = val;
}
}
}
</script>
<script module="whole" lang="renderjs">
let code;
let KeyName = {
19: 'KeyUp',
38: 'KeyUp', //Keyboard
20: 'KeyDown',
40: 'KeyDown', //Keyboard
21: 'KeyLeft',
37: 'KeyLeft', //Keyboard
22: 'KeyRight',
39: 'KeyRight', //Keyboard
23: 'KeyEnter',
13: 'KeyEnter', //Keyboard
4: 'KeyBack',
18: 'KeyBack', //Keyboard Alt
27: 'KeyBack', //Keyboard ESC
24: 'KeyBack', //Keyboard ESC
66: 'KeyEnter',
111: 'KeyBack'
};
export default {
mounted() {
//
window.document.onkeydown = function(evt) {
evt = evt || window.event;
var KeyCode = evt.which || evt.keyCode;
code = KeyName[KeyCode];
evt.preventDefault();
if (code != undefined) {
document.getElementById("onClick").click();
}
}
},
methods: {
onClick(event, ownerInstance) {
event.preventDefault();
ownerInstance.callMethod('keyCodeClick', code);
}
}
}
</script>
<style scoped>
.log-key-view {
position: fixed;
right: 0;
color: #FFFFFF;
font-size: 20rpx;
background: #007AFF;
top: 0;
z-index: 999;
}
</style>

View File

@ -0,0 +1,58 @@
<template>
<scroll-view :scroll-x="true" :scroll-left="scrollLeft" @scroll="scroll">
<slot></slot>
</scroll-view>
</template>
<script>
export default {
inject: ['pageId', 'zoneId', 'zoneState'],
watch: {
},
data() {
return {
scrollLeft: 0,
old: {
scrollLeft: 0
},
windowWidth: null
};
},
created() {
this.zoneState.curScroll = this;
uni.getSystemInfo({
success: (res) => {
this.windowWidth = res.windowWidth;
}
});
},
methods: {
scroll: function(e) {
this.$emit("scroll", {
oldLeft: this.old.scrollLeft,
left: e.detail.scrollLeft,
})
this.old.scrollLeft = e.detail.scrollLeft;
},
RefreshScrollLeft(clientRect) {
var left = clientRect.left;
if (left > 0) {
left = clientRect.right;
}
if (left > this.windowWidth) {
var left1 = this.old.scrollLeft + (left - this.windowWidth + 360);
this.scrollLeft = left1
} else if (left < 0) {
this.scrollLeft = this.old.scrollLeft + (left - 380);
}
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,280 @@
<template>
<view v-if="isShow" ref="ani" class="uni-transition" :class="[ani.in]" :style="'transform:' +transform+';'+stylesObject"
@click="change">
<slot></slot>
</view>
</template>
<script>
// #ifdef APP-NVUE
const animation = uni.requireNativePlugin('animation');
// #endif
/**
* Transition 过渡动画
* @description 简单过渡动画组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
* @property {Array} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
* @value fade 渐隐渐出过渡
* @value slide-top 由上至下过渡
* @value slide-right 由右至左过渡
* @value slide-bottom 由下至上过渡
* @value slide-left 由左至右过渡
* @value zoom-in 由小到大过渡
* @value zoom-out 由大到小过渡
* @property {Number} duration 过渡动画持续时间
* @property {Object} styles 组件样式 css 样式注意带-连接符的属性需要使用小驼峰写法如`backgroundColor:red`
*/
export default {
name: 'uniTransition',
props: {
show: {
type: Boolean,
default: false
},
modeClass: {
type: Array,
default () {
return []
}
},
duration: {
type: Number,
default: 300
},
styles: {
type: Object,
default () {
return {}
}
}
},
data() {
return {
isShow: false,
transform: '',
ani: { in: '',
active: ''
}
};
},
watch: {
show: {
handler(newVal) {
if (newVal) {
this.open()
} else {
this.close()
}
},
immediate: true
}
},
computed: {
stylesObject() {
let styles = {
...this.styles,
'transition-duration': this.duration / 1000 + 's'
}
let transfrom = ''
for (let i in styles) {
let line = this.toLine(i)
transfrom += line + ':' + styles[i] + ';'
}
return transfrom
}
},
created() {
// this.timer = null
// this.nextTick = (time = 50) => new Promise(resolve => {
// clearTimeout(this.timer)
// this.timer = setTimeout(resolve, time)
// return this.timer
// });
},
methods: {
change() {
this.$emit('click', {
detail: this.isShow
})
},
open() {
clearTimeout(this.timer)
this.isShow = true
this.transform = ''
this.ani.in = ''
for (let i in this.getTranfrom(false)) {
if (i === 'opacity') {
this.ani.in = 'fade-in'
} else {
this.transform += `${this.getTranfrom(false)[i]} `
}
}
this.$nextTick(() => {
setTimeout(() => {
this._animation(true)
}, 50)
})
},
close(type) {
clearTimeout(this.timer)
this._animation(false)
},
_animation(type) {
let styles = this.getTranfrom(type)
// #ifdef APP-NVUE
if(!this.$refs['ani']) return
animation.transition(this.$refs['ani'].ref, {
styles,
duration: this.duration, //ms
timingFunction: 'ease',
needLayout: false,
delay: 0 //ms
}, () => {
if (!type) {
this.isShow = false
}
this.$emit('change', {
detail: this.isShow
})
})
// #endif
// #ifndef APP-NVUE
this.transform = ''
for (let i in styles) {
if (i === 'opacity') {
this.ani.in = `fade-${type?'out':'in'}`
} else {
this.transform += `${styles[i]} `
}
}
this.timer = setTimeout(() => {
if (!type) {
this.isShow = false
}
this.$emit('change', {
detail: this.isShow
})
}, this.duration)
// #endif
},
getTranfrom(type) {
let styles = {
transform: ''
}
this.modeClass.forEach((mode) => {
switch (mode) {
case 'fade':
styles.opacity = type ? 1 : 0
break;
case 'slide-top':
styles.transform += `translateY(${type?'0':'-100%'}) `
break;
case 'slide-right':
styles.transform += `translateX(${type?'0':'100%'}) `
break;
case 'slide-bottom':
styles.transform += `translateY(${type?'0':'100%'}) `
break;
case 'slide-left':
styles.transform += `translateX(${type?'0':'-100%'}) `
break;
case 'zoom-in':
styles.transform += `scale(${type?1:0.8}) `
break;
case 'zoom-out':
styles.transform += `scale(${type?1:1.2}) `
break;
}
})
return styles
},
_modeClassArr(type) {
let mode = this.modeClass
if (typeof(mode) !== "string") {
let modestr = ''
mode.forEach((item) => {
modestr += (item + '-' + type + ',')
})
return modestr.substr(0, modestr.length - 1)
} else {
return mode + '-' + type
}
},
// getEl(el) {
// console.log(el || el.ref || null);
// return el || el.ref || null
// },
toLine(name) {
return name.replace(/([A-Z])/g, "-$1").toLowerCase();
}
}
}
</script>
<style>
.uni-transition {
transition-timing-function: ease;
transition-duration: 0.3s;
transition-property: transform, opacity;
z-index: 998;
}
.fade-in {
opacity: 0;
}
.fade-active {
opacity: 1;
}
.slide-top-in {
/* transition-property: transform, opacity; */
transform: translateY(-100%);
}
.slide-top-active {
transform: translateY(0);
/* opacity: 1; */
}
.slide-right-in {
transform: translateX(100%);
}
.slide-right-active {
transform: translateX(0);
}
.slide-bottom-in {
transform: translateY(100%);
}
.slide-bottom-active {
transform: translateY(0);
}
.slide-left-in {
transform: translateX(-100%);
}
.slide-left-active {
transform: translateX(0);
opacity: 1;
}
.zoom-in-in {
transform: scale(0.8);
}
.zoom-out-active {
transform: scale(1);
}
.zoom-out-in {
transform: scale(1.2);
}
</style>

View File

@ -0,0 +1,147 @@
<template>
<video id="player" :class="clazz" :src="src" :autoplay="autoplay" :initial-time="initialTime" :enable-play-gesture="false" :title="title"
:enable-progress-gesture="false" :show-center-play-btn="false" :show-fullscreen-btn="false" :show-progress="true" @play="status=1"
@pause="status=0" @timeupdate="timeupdate" @fullscreenchange="fullscreenchange" :poster="pauseImg"
@ended="$emit('ended')" @error="errorFun" width="100%" height="100%" style="width: 100%;height: 100%;">
<cover-image src="../../static/loading_1920.gif" class="loading_box" v-if="isSHowLoading"
:play-strategy="2"></cover-image>
</video>
</template>
<script>
export default {
inject: ['pageId', 'pageState'],
watch: {
},
props: {
clazz: {
type: String
},
title: {
type: String,
default: ""
},
src: {
type: String
},
autoplay: {
type: Boolean,
default: true
},
showProgress: {
type: Boolean,
default: false
},
isSHowLoading: {
type: Boolean,
default: false
},
pauseImg: {
type: String,
default: ""
},
failStatus: {
type: Number,
default: 0
},
initialTime: {
type: Number,
default: 0
}
},
data() {
return {
status: 0,
currentTime: 0,
fullScreen: false,
};
},
mounted() {
this.viderContext = uni.createVideoContext("player", this);
},
onLoad() {
uni.$on('keyDown', this.keyDownFun);
},
onUnload() {
uni.$off('keyDown', this.keyDownFun);
},
methods: {
keyDownFun(data) {
console.log("keyDownFun")
this.keyDown(data);
},
keyDown(Arrow) {
console.log("监测到按键:" + Arrow)
switch (Arrow) {
case 'KeyLeft': {
var time = parseInt(this.currentTime - 15, 10);
this.viderContext.seek(time < 0 ? 0 : time)
}
break;
case 'KeyRight': {
var time = parseInt(this.currentTime + 15, 10);
this.viderContext.seek(time);
}
break;
case 'KeyEnter': {
this.clickVideo();
}
break;
};
},
clickVideo() {
if (this.status) {
this.viderContext.pause();
this.viderContext.showStatusBar();
} else {
this.viderContext.play();
this.viderContext.hideStatusBar();
}
},
timeupdate: function(e) {
this.currentTime = e.detail.currentTime;
this.$emit("timeupdate", e.detail.currentTime)
},
fullscreenchange: function(e) {
this.fullScreen = e.detail.fullScreen;
this.pageState.handleEvent = e.detail.fullScreen;
uni.$off('keyDown', this.keyDownFun);
if (this.fullScreen) {
this.viderContext.play();
uni.$on('keyDown', this.keyDownFun);
}
this.$emit("fullscreenchange", e.detail.fullScreen)
},
errorFun() {
this.$emit("errorFun")
},
}
}
</script>
<style>
.controls-play.img {
position: absolute;
width: 100%;
height: 100%;
top: 50%;
left: 0%;
transform: translateY(-50%);
}
.loading_box {
position: fixed;
width: 100vw;
height: 100vh;
left: 0px;
top: 0px;
z-index: 100;
}
.fail_img {
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,255 @@
<template>
<view :id="id">
<slot></slot>
</view>
</template>
<script>
import {
mapMutations,
mapActions,
mapState
} from 'vuex';
export default {
name: "unitvZone",
inject: ['pageId', 'pageState'],
props: {
id: { // Zone Id
type: String,
required: true
},
up: { // ZoneZone Id
type: String
},
down: { // ZoneZone Id
type: String
},
left: { // ZoneZone Id
type: String
},
right: { // ZoneZone Id
type: String
},
item: { // Zoneitem
type: Number,
default: 0
},
prevZone: { // ZoneZone Id
type: String
},
row: { // Zoneitem
type: Number,
default: 1
},
column: { // Zoneitem
type: Number,
default: 1
},
defaultBehavior: {
type: Boolean,
default: true
},
autoFous: {
type: Boolean,
default: false
},
values: {
type: Array,
default: () => []
},
count: {
type: Number,
default: 0
}
},
provide() {
return {
zoneId: this.id,
zoneState: this.zoneState,
zoneItems: this.items
}
},
created() {
//console.log(this.pageId)
if (this.autoFous) {
this.pageState.curZoneId = this.id;
this.zoneState.curZone = true;
this.focus = true;
this.switchZone(this);
}
this._switchItem(this.item);
this.currentPage.pushZone(this);
},
mounted() {
},
computed: {
...mapState(['currentZone', 'currentPage']),
size: function() {
if (this.count != 0) {
return this.count;
} else if (this.values.length != 0) {
return this.values.length;
} else {
return this.items.length;
}
},
rows: function() {
if (this.row > 1) {
return this.row;
}
return Math.ceil(this.size / this.column)
}
},
data() {
return {
citem: 0,
crow: 0, // Zone
prevItem: 0, // itemitem
focus: false,
items: [], // Zoneitem
zoneState: {
curItem: this.citem,
curZone: this.focus,
curScroll: null
},
StepSeq: 0
}
},
methods: {
...mapMutations(['switchZone']),
_switchItem: function(item, oldItem) {
this.citem = item;
this.zoneState.curItem = item;
try {
this.refreshItem();
if (oldItem != undefined) {
this.items[oldItem].refreshState();
}
} catch (e) {}
},
refreshItem: function() {
this.items[this.citem].refreshState();
},
evtArrow: function(Arrow) {
var self = this;
//this._switchItem(this.citem+1);
var oldItem = this.citem || 0;
var item = this.citem || 0;
var steps = this.rows * this.column;
var Row = Math.floor(item / this.column);
var Border = this[Arrow];
var cRow = this.crow;
switch (Arrow) {
case 'left':
item -= 1;
if (Math.floor(item / this.column) != Row) {
item = item - steps;
}
break;
case 'right':
item += 1;
if (Math.floor(item / this.column) != Row) {
item = item + steps;
}
break;
case 'up':
item -= this.column;
this.crow = cRow - 1;
break;
case 'down':
item += this.column
this.crow = cRow + 1;
if (item >= this.size - 1 && this.crow === this.rows - 1) {
item = this.size - 1
}
break;
};
if (item >= 0 && item <= this.size - 1) {
if (item + this.StepSeq * steps + 1 > this.size) {
item = this.size - this.StepSeq * steps - 1;
}
this.citem = item;
this._switchItem(this.citem, oldItem);
if (this.crow === (this.rows - 1)) {
this.$emit("scrolltolower");
}
} else {
this.crow = cRow;
OverBorder();
};
function OverBorder() {
if (Border) {
if (Border === self.id) {
ScrollItem();
} else {
ChangeZone();
}
};
function ScrollItem() {
switch (Arrow) {
case 'up':
item = self.citem + self.column * (self.rows - 1);
self.crow = self.rows - 1;
break
case 'down':
item = self.citem - self.column * (self.rows - 1);
self.crow = 0;
break
case 'left':
item = (Row + 1) * self.column - 1;
break
case 'right':
item = Row * self.column;
break
};
//
if (self.size > steps) {
self.StepSeq = self.StepSeq || 0;
var MaxSeq = Math.ceil(self.size / steps) - 1;
if ((Arrow === 'left') || (Arrow === 'up')) {
//
self.StepSeq = (self.StepSeq > 0) ? self.StepSeq - 1 : MaxSeq;
} else if ((Arrow === 'right') || (Arrow === 'down')) {
//
self.StepSeq = (self.StepSeq < MaxSeq) ? self.StepSeq + 1 : 0;
};
};
//
if (item + self.StepSeq * steps + 1 > self.size) {
item = 0;
}
self.citem = item;
//
self._switchItem(self.citem, oldItem);
};
function ChangeZone() {
self.currentPage.ChangeZone(Border);
self.refreshItem();
self.$emit("swtichZone", self.zoneState)
};
};
},
evtEnter: function() {
var itemObj = this.items[this.citem];
if (itemObj) {
itemObj.handleClick();
}
}
},
watch: {
values: function(newVal, oldVal) {
}
}
}
</script>
<style scoped>
</style>

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>

30
main.js Normal file
View File

@ -0,0 +1,30 @@
import App from "./App";
import store from "@/store/index.js";
import "dayjs/locale/zh-cn";
// #ifndef VUE3
import Vue from "vue";
// #endif
// #ifdef VUE3
import { createSSRApp } from "vue";
// #endif
// #ifndef VUE3
Vue.config.productionTip = false;
App.mpType = "app";
const app = new Vue({
...App,
store,
});
app.$mount();
// #endif
// #ifdef VUE3
export function createApp() {
const app = createSSRApp(App);
app.use(store);
return {
app,
};
}
// #endif

124
manifest.json Normal file
View File

@ -0,0 +1,124 @@
{
"name" : "金安铸云",
"appid" : "__UNI__9209C12",
"description" : "",
"versionName" : "1.0.1",
"versionCode" : "101",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"orientation" : [ "landscape-primary" ],
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"nvueCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {
"VideoPlayer" : {}
},
/* */
"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\"/>"
],
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ],
"targetSdkVersion" : 28
},
/* ios */
"ios" : {
"dSYMs" : false
},
/* SDK */
"sdkConfigs" : {
"ad" : {}
},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
}
},
"splashscreen" : {
"androidStyle" : "default",
"android" : {
"hdpi" : "static/bg.9.png",
"xhdpi" : "static/bg.9.png",
"xxhdpi" : "static/bg.9.png"
}
}
}
},
/* */
"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",
"fallbackLocale" : "zh-Hans",
"locale" : "zh-Hans"
}

3079
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

21
package.json Normal file
View File

@ -0,0 +1,21 @@
{
"dependencies": {
"dayjs": "^1.11.13",
"uqrcodejs": "^4.0.7"
},
"devDependencies": {
"@types/html5plus": "^1.0.5",
"@types/uni-app": "^1.4.8",
"@vue/eslint-config-prettier": "^7.1.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^8.10.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.19.2",
"prettier": "^2.8.8",
"vue-eslint-parser": "^9.3.2"
}
}

66
pages.json Normal file
View File

@ -0,0 +1,66 @@
{
"easycom": {
"autoscan": true,
"custom": {
"unitv-(.*)" : "@/components/unitv-$1/unitv-$1.vue",
"app-(.*)" : "@/components/app-$1/index.vue"
}
},
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/curriculum/details",
"style": {
"navigationBarTitleText": "课程详情"
}
},
{
"path": "pages/curriculum/play",
"style": {
"navigationBarTitleText": "视频播放"
}
},
{
"path": "pages/curriculum/buy",
"style": {
"navigationBarTitleText": "课程购买"
}
},
{
"path": "pages/curriculum/buy_success",
"style": {
"navigationBarTitleText": "购买成功"
}
},
{
"path": "pages/mine/index",
"style": {
"navigationBarTitleText": "我的"
}
},
{
"path": "pages/mine/curriculum/index",
"style": {
"navigationBarTitleText": "我的课程"
}
}
],
"globalStyle": {
"navigationStyle": "custom",//
"rpxCalcMaxDeviceWidth": 2560, // rpx px 960
"rpxCalcBaseDeviceWidth": 1920, // rpx 使 rpx px 375
"rpxCalcIncludeWidth": 2560 // rpx rpx 750
},
"uniIdRouter": {}
}

158
pages/curriculum/buy.vue Normal file
View File

@ -0,0 +1,158 @@
<template>
<view class="container">
<unitv-page
id="curriculumBuyPage"
ref="curriculumBuyPage"
class="curriculumBuyPage"
:show="true"
>
<app-header />
<view class="content">
<view class="left">
<view class="shop">
<image src="/static/public_images/video_bg1.jpg" />
<view class="name line-4">
课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称课程名称
</view>
</view>
<view class="money">
<view class="price">123</view>
<view class="tip">购买此课程</view>
</view>
</view>
<view class="qrcode">
<view class="image">
<view class="payee">收款方河北秦安安全科技有限公司</view>
<canvas
id="qrcode"
canvas-id="qrcode"
:style="{ width: canvasSize + 'px', height: canvasSize + 'px' }"
/>
<view class="tip">使用微信扫一扫支付</view>
</view>
</view>
</view>
</unitv-page>
</view>
</template>
<script>
import UQRCode from "uqrcodejs";
export default {
data() {
return {
canvasSize: uni.upx2px(170),
};
},
onShow() {
if (this.$refs.curriculumBuyPage) {
this.$refs.curriculumBuyPage.showPage();
}
},
onReady() {
const qr = new UQRCode();
qr.data = "qrcode_url";
qr.size = this.canvasSize;
qr.make();
qr.canvasContext = uni.createCanvasContext("qrcode");
qr.drawCanvas();
// setTimeout(() => {
// uni.navigateTo({
// url: "/pages/curriculum/buy_success",
// });
// }, 10000);
},
};
</script>
<style scoped lang="scss">
.content {
display: flex;
align-items: center;
padding: 61rpx 77rpx 54rpx 45rpx;
.left {
width: 355rpx;
.shop {
width: 355rpx;
height: 124rpx;
background-image: url("/static/by_shop_bg.png");
background-size: 100% 100%;
background-repeat: no-repeat;
display: flex;
padding: 24rpx 12rpx;
image {
width: 133rpx;
height: 75rpx;
border-radius: 4rpx;
}
.name {
flex: 1;
font-size: 14rpx;
padding-left: 12rpx;
}
}
.money {
margin-top: 10rpx;
width: 355rpx;
height: 54rpx;
background-image: url("/static/by_shop_money_bg.png");
background-size: 100% 100%;
background-repeat: no-repeat;
position: relative;
color: rgba(115, 62, 2, 1);
font-weight: bold;
.price {
font-size: 24rpx;
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 17rpx;
}
.tip {
font-size: 14rpx;
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 121rpx;
}
}
}
.qrcode {
margin-left: 43rpx;
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.17);
border-radius: 4px;
padding: 12rpx;
.image {
background-color: #fff;
padding: 12rpx;
color: #000;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
.payee {
padding-bottom: 6rpx;
font-size: 9rpx;
}
.tip {
padding-top: 7rpx;
color: rgba(7, 15, 48, 1);
font-size: 13rpx;
}
}
}
}
</style>

View File

@ -0,0 +1,105 @@
<template>
<view class="container">
<unitv-page
id="curriculumBuySuccessPage"
ref="curriculumBuySuccessPage"
class="curriculumBuySuccessPage"
:show="true"
>
<app-header down="curriculum_buy_success_first_zone" />
<view class="content">
<view class="image">
<image src="/static/buy_success.png" />
</view>
<view class="tip">订单支付成功请在我的课程中查看</view>
<view class="info">
<view>订单编号20241028153636598755</view>
<view>订单名称安全视频 消防安全基础知识培训课程</view>
<view>订单时间2024-10-28 15:06</view>
<view>订单价格365</view>
</view>
<unitv-zone
id="curriculum_buy_success_first_zone"
class="curriculum_buy_success_first_zone"
up="header_zone"
:values="[0]"
:column="1"
auto-fous
:item="0"
>
<unitv-item :item="0" class="item">
<view class="text">我的课程</view>
</unitv-item>
</unitv-zone>
</view>
</unitv-page>
</view>
</template>
<script>
export default {
onShow() {
if (this.$refs.curriculumBuySuccessPage) {
this.$refs.curriculumBuySuccessPage.showPage();
}
},
};
</script>
<style scoped lang="scss">
.container {
.content {
width: 400rpx;
margin: 29rpx auto auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.image {
width: 113rpx;
height: 116rpx;
image {
width: 100%;
height: 100% !important;
}
}
.tip {
font-size: 12rpx;
}
.info {
padding: 18rpx 100rpx 16rpx 18rpx;
margin-top: 25rpx;
background-color: rgba(255, 255, 255, 0.05);
border-radius: 4rpx;
border: 1rpx solid rgba(255, 255, 255, 0.1);
view {
margin-top: 5rpx;
}
}
.curriculum_buy_success_first_zone {
margin-top: 15rpx;
.item {
background-color: rgba(37, 78, 255, 1);
border-radius: 2rpx;
width: 190rpx;
height: 38rpx;
font-size: 14rpx;
text-align: center;
line-height: 38rpx;
border: 2rpx solid transparent;
&.hover {
border: 2rpx solid rgba(255, 255, 255, 0.5);
}
}
}
}
}
</style>

View File

@ -0,0 +1,52 @@
export default [
{
name: "基础知识",
nodes: [
{
coursewareName: "气体基础知识",
url: "https://video.qhdsafety.com/sv/5b72c64c-192e53dc244/5b72c64c-192e53dc244.mp4",
},
{
coursewareName: "现场一线员工全面安全培训",
url: "https://video.qhdsafety.com/sv/5eeb67f3-192e099ebba/5eeb67f3-192e099ebba.mp4",
},
{
coursewareName: "出差人员基础安全知识与应急处置",
url: "https://video.qhdsafety.com/sv/40c0369f-192e099692d/40c0369f-192e099692d.mp4",
},
{
coursewareName: "现场安全负责人全面安全培训",
url: "https://video.qhdsafety.com/sv/6b9ef0b-192e098bf81/6b9ef0b-192e098bf81.mp4",
},
{
coursewareName: "危险货物运输包装-气瓶",
url: "https://video.qhdsafety.com/sv/a5a0ad5-192e06394c6/a5a0ad5-192e06394c6.mp4",
},
{
coursewareName: "职业病危害专项治理",
url: "https://video.qhdsafety.com/sv/5714ae67-1930b695a05/5714ae67-1930b695a05.mp4",
},
],
},
{
name: "安全生产条例",
nodes: [
{
coursewareName: "《四川省安全生产条例》特色解读",
url: "https://video.qhdsafety.com/sv/478dfce2-192e08e3216/478dfce2-192e08e3216.mp4",
},
{
coursewareName: "企业贯彻落实《四川省安全生产条例》精要",
url: "https://video.qhdsafety.com/sv/4289cf05-192e08cdd4d/4289cf05-192e08cdd4d.mp4",
},
{
coursewareName: "《四川省安全生产举报奖励办法》解读",
url: "https://video.qhdsafety.com/sv/1ebad0e6-192e08b8700/1ebad0e6-192e08b8700.mp4",
},
{
coursewareName: "应急管理部关于进一步加强安全生产举报工作的指导意见",
url: "https://video.qhdsafety.com/sv/1c845c96-192e08a5b4b/1c845c96-192e08a5b4b.mp4",
},
],
},
];

View File

@ -0,0 +1,522 @@
<template>
<view class="container">
<unitv-page
id="curriculumDetailsPage"
ref="curriculumDetailsPage"
class="curriculumDetailsPage"
:show="true"
>
<app-header down="curriculum_details_first_zone" />
<view class="curriculumInfo">
<view class="info">
<view class="curriculumName">
{{ curriculumInfo.curriculumName }}
</view>
<view class="introduction line-3">
{{ curriculumInfo.introduction }}
</view>
<unitv-zone
id="curriculum_details_first_zone"
class="curriculum_details_first_zone"
up="header_zone"
down="curriculum_details_second_zone"
:values="[0, 1]"
:column="2"
>
<unitv-item :item="0" class="item trySee" @click="fnFullScreen">
<view class="icon">
<image src="/static/bofang.png" />
</view>
<view class="text">全屏试看</view>
</unitv-item>
<unitv-item :item="1" class="item buy" @click="fnBuy">
<view class="price">{{ curriculumInfo.price }}</view>
<view class="text">购买课程</view>
</unitv-item>
</unitv-zone>
</view>
<view v-if="!video.url" class="cover">
<image :src="curriculumInfo.cover" mode="widthFix" />
</view>
<view v-if="video.url && !isFullScreen" class="cover">
<unitv-video
id="player"
:src="video.url"
:title="video.title"
:initial-time="video.time"
@timeupdate="fnTimeupdate"
/>
</view>
</view>
<view class="chapter">
<view class="main_title">
<text>章节</text>
<text class="current">{{ currentChapterInfo.name }}</text>
</view>
<unitv-zone
id="curriculum_details_second_zone"
:key="currentChapterIndex"
class="curriculum_details_second_zone"
up="curriculum_details_first_zone"
down="curriculum_details_third_zone"
:values="currentChapterInfo.nodes"
:column="currentChapterInfo.nodes.length"
>
<unitv-scroll>
<view class="scroll">
<unitv-item
v-for="(item, index) in currentChapterInfo.nodes"
:key="index"
:item="index"
class="item"
@click="fnPlayVideo(item)"
>
<view class="text">{{ item.coursewareName }}</view>
</unitv-item>
</view>
</unitv-scroll>
</unitv-zone>
<unitv-zone
id="curriculum_details_third_zone"
class="curriculum_details_third_zone"
up="curriculum_details_second_zone"
down="curriculum_details_fourth_zone"
:values="chapterList"
:column="chapterList.length"
auto-fous
:item="0"
>
<unitv-scroll>
<view class="scroll">
<unitv-item
v-for="(item, index) in chapterList"
:key="index"
:item="index"
class="item"
@click="fnClickChapterItem"
>
<view class="text">{{ item.name }}</view>
<view
v-if="index !== chapterList.length - 1"
class="border_right"
/>
<view class="border_bottom" />
</unitv-item>
</view>
</unitv-scroll>
</unitv-zone>
</view>
<view class="recommend_curriculum">
<view class="main_title">为你推荐</view>
<unitv-zone
id="curriculum_details_fourth_zone"
class="curriculum_details_fourth_zone"
up="curriculum_details_third_zone"
:values="recommendCurriculumList"
:column="recommendCurriculumList.length"
>
<unitv-scroll>
<view class="scroll">
<unitv-item
v-for="(item, index) in recommendCurriculumList"
:key="index"
:item="index"
class="item"
@click="fnSwitchCurriculum"
>
<image :src="item.img" mode="widthFix" />
<view class="title line-1">{{ item.title }}</view>
</unitv-item>
</view>
</unitv-scroll>
</unitv-zone>
</view>
</unitv-page>
</view>
</template>
<script>
import chapterList from "./chapterList";
export default {
data() {
return {
curriculumInfo: {
curriculumName: "气体基础知识",
introduction:
"气体是指无形状有体积的可压缩和膨胀的流体。气体是物质的一个态,气体与液体一样是流体:它可以流动,可变形。与液体不同的是气体气体分子间距离很大,可以被压缩膨胀。假如没有限制(容器或力场)的话,气体可以膨胀,其体积不受限制。气态物质的原子或分子相互之间可以自由运动。气态物质的原子或分子的动能比较高。 气体形态可通过其体积、温度和其压强所影响。这几项要素构成了多项气体定律,而三者之间又可以互相影响。",
cover: require("../../static/public_images/video_bg1.jpg"),
price: "123",
},
video: {
url: "",
title: "",
time: 0,
},
chapterList,
currentChapterIndex: 0,
currentChapterInfo: {},
recommendCurriculumList: [],
videoContext: null,
isFullScreen: false,
};
},
onShow() {
if (this.$refs.curriculumDetailsPage) {
this.$refs.curriculumDetailsPage.showPage();
if (this.videoContext) {
this.videoContext.play();
}
}
},
onHide() {
this.videoContext && this.videoContext.pause();
},
onLoad() {
this.fnInitCurrentChapter();
this.fnGetRecommendCurriculum();
},
methods: {
fnInitCurrentChapter() {
this.currentChapterInfo = this.chapterList[this.currentChapterIndex];
},
fnClickChapterItem(event) {
this.currentChapterIndex = event;
this.fnInitCurrentChapter();
},
async fnGetRecommendCurriculum() {
this.recommendCurriculumList.push({
img: require(`../../static/public_images/video_bg1.jpg`),
title: "气体基础知识",
});
this.recommendCurriculumList.push({
img: require(`../../static/public_images/video_bg2.jpg`),
title: "现场一线员工全面安全培训",
});
this.recommendCurriculumList.push({
img: require(`../../static/public_images/video_bg3.jpg`),
title: "出差人员基础安全知识与应急处置",
});
this.recommendCurriculumList.push({
img: require(`../../static/public_images/video_bg4.jpg`),
title: "现场安全负责人全面安全培训",
});
this.recommendCurriculumList.push({
img: require(`../../static/public_images/video_bg5.jpg`),
title: "《四川省安全生产条例》特色解读",
});
this.recommendCurriculumList.push({
img: require(`../../static/public_images/video_bg6.jpg`),
title: "企业贯彻落实《四川省安全生产条例》精要",
});
this.recommendCurriculumList.push({
img: require(`../../static/public_images/video_bg7.jpg`),
title: "《四川省安全生产举报奖励办法》解读",
});
this.recommendCurriculumList.push({
img: require(`../../static/public_images/video_bg8.jpg`),
title: "应急管理部关于进一步加强安全生产举报工作的指导意见",
});
this.recommendCurriculumList.push({
img: require(`../../static/public_images/video_bg9.jpg`),
title: "危险货物运输包装-气瓶",
});
this.recommendCurriculumList.push({
img: require(`../../static/public_images/video_bg10.jpg`),
title: "职业病危害专项治理",
});
},
async fnPlayVideo(item) {
this.video.url = "";
await this.$nextTick();
this.video.url = item.url;
this.video.title = item.coursewareName;
this.video.time = 0;
await this.$nextTick();
!this.videoContext &&
(this.videoContext = uni.createVideoContext("player"));
},
fnTimeupdate(time) {
this.video.time = time;
},
fnFullScreen() {
this.isFullScreen = true;
this.videoContext = null;
const { url, time, title } = this.video;
uni.navigateTo({
url: `/pages/curriculum/play?url=${url}&title=${title}&time=${time}`,
animationType: "none",
events: {
timeupdate: async ({ time }) => {
this.isFullScreen = false;
this.video.time = time;
await this.$nextTick();
!this.videoContext &&
(this.videoContext = uni.createVideoContext("player"));
},
},
});
},
fnBuy() {
uni.navigateTo({ url: "/pages/curriculum/buy" });
},
fnSwitchCurriculum() {
uni.redirectTo({
url: "/pages/curriculum/details",
});
},
},
};
</script>
<style scoped lang="scss">
.container {
.curriculumInfo {
margin-top: 10rpx;
display: flex;
.info {
flex: 1;
.curriculumName {
font-size: 18rpx;
font-weight: bold;
}
.introduction {
font-size: 12rpx;
padding-top: 5rpx;
}
.curriculum_details_first_zone {
margin-top: 10rpx;
display: flex;
.trySee {
width: 53rpx;
height: 53rpx;
background: rgba(255, 255, 255, 0.1);
border-radius: 1rpx;
border: 1px solid rgba(207, 207, 247, 0.1);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.icon {
margin-bottom: 5rpx;
image {
width: 15rpx;
height: 18rpx;
}
}
.text {
font-size: 9rpx;
}
&.hover {
border: 1rpx solid rgba(0, 180, 255, 0.79);
}
}
.buy {
margin-left: 14rpx;
width: 53rpx;
height: 53rpx;
background-image: url("/static/buy_bg.png");
background-size: 100% 100%;
background-repeat: no-repeat;
border-radius: 1rpx;
text-align: center;
padding: 8rpx;
border: 1rpx solid transparent;
.price {
color: rgba(115, 62, 2, 1);
font-size: 14rpx;
font-weight: bold;
}
.text {
margin-top: 8rpx;
font-size: 9rpx;
color: rgba(85, 45, 0, 1);
font-weight: bold;
}
&.hover {
border: 1rpx solid rgba(0, 180, 255, 0.79);
}
}
}
}
.cover {
margin-left: 13rpx;
width: 343rpx;
height: 150rpx;
image {
width: 100%;
height: 100% !important;
}
}
}
.chapter {
margin-top: 10rpx;
.main_title {
font-size: 14rpx;
.current {
margin-left: 9rpx;
font-size: 9rpx;
}
}
.curriculum_details_second_zone {
margin-top: 5rpx;
.scroll {
display: flex;
white-space: nowrap;
.item {
margin-left: 10rpx;
padding: 5rpx 10rpx;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 1rpx;
&:first-child {
margin-left: 0;
}
.text {
font-size: 9rpx;
}
&.hover {
background-color: #254eff;
}
&.active {
background-color: rgba(37, 78, 255, 0.39);
}
}
}
}
.curriculum_details_third_zone {
margin-top: 5rpx;
.scroll {
display: flex;
white-space: nowrap;
.item {
display: flex;
align-items: center;
position: relative;
.border_right {
width: 1rpx;
background-color: #fff;
height: 10rpx;
}
.border_bottom {
width: 60%;
background-color: transparent;
height: 1rpx;
position: absolute;
left: 20%;
right: 20%;
bottom: 0;
}
&:first-child {
.text {
padding-left: 0;
}
.border_bottom {
left: calc(20% - 5rpx);
}
}
.text {
font-size: 9rpx;
border-bottom: 1rpx solid transparent;
padding: 5rpx 10rpx;
}
&.hover {
.border_bottom {
background-color: #254eff;
}
}
&.active {
.border_bottom {
background-color: #fff;
}
}
}
}
}
}
.recommend_curriculum {
margin-top: 10rpx;
.main_title {
font-size: 14rpx;
}
.curriculum_details_fourth_zone {
margin-top: 5rpx;
.scroll {
white-space: nowrap;
.item {
display: inline-block;
width: calc(18% - 20rpx);
height: 100rpx;
position: relative;
border: 2px solid transparent;
border-radius: 4rpx;
font-size: 0;
margin-left: 20rpx;
&:first-child {
margin-left: 0;
}
image {
width: 100%;
height: 100% !important;
border-radius: 4rpx;
}
.title {
width: 80%;
font-size: 9rpx;
color: #fff;
position: absolute;
bottom: 9rpx;
left: 7rpx;
}
&.hover {
border: 2rpx solid rgba(0, 180, 255, 0.79);
}
}
}
}
}
}
</style>

50
pages/curriculum/play.vue Normal file
View File

@ -0,0 +1,50 @@
<template>
<unitv-page
id="curriculumPlayPage"
ref="curriculumPlayPage"
class="curriculumPlayPage"
:show="true"
>
<unitv-video
id="player"
:src="url"
:title="title"
:initial-time="time"
@timeupdate="fnTimeupdate"
/>
</unitv-page>
</template>
<script>
export default {
data() {
return {
videoContext: null,
url: "",
title: "",
time: 0,
};
},
onLoad(query) {
this.url = query.url;
this.title = query.title;
this.time = +query.time;
},
onReady() {
this.videoContext = uni.createVideoContext("player");
this.videoContext.play();
this.videoContext.seek(this.time);
},
onUnload() {
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit("timeupdate", { time: this.time });
},
methods: {
fnTimeupdate(time) {
this.time = time;
},
},
};
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,364 @@
<template>
<view class="content">
<!-- <app-loading v-if="loading" />-->
<view class="system">
<unitv-zone
id="course_classification_first_zone"
class="course_classification_first_zone"
up="tabs_zone"
right="course_classification_second_zone"
:values="systemList"
:column="1"
:row="systemList.length"
>
<unitv-item
v-for="(item, index) in systemList"
:key="index"
:item="index"
class="item"
@click="fnClickSystem"
>
<view class="title line-1">{{ item.title }}</view>
</unitv-item>
</unitv-zone>
</view>
<view class="right">
<view class="search">
<view class="block">
<view class="main_title">最新</view>
<unitv-zone
id="course_classification_second_zone"
class="search_zone"
up="tabs_zone"
down="course_classification_third_zone"
left="course_classification_first_zone"
:values="newestList"
:column="newestList.length"
>
<unitv-item
v-for="(item, index) in newestList"
:key="index"
:item="index"
class="item"
@click="fnClickSearch($event, 'newestList')"
>
<view class="title">{{ item.title }}</view>
</unitv-item>
</unitv-zone>
</view>
<view class="block">
<view class="main_title">行业</view>
<unitv-zone
id="course_classification_third_zone"
class="search_zone"
up="course_classification_second_zone"
down="course_classification_fourth_zone"
left="course_classification_first_zone"
:values="industryList"
:column="industryList.length"
>
<unitv-item
v-for="(item, index) in industryList"
:key="index"
:item="index"
class="item"
@click="fnClickSearch($event, 'industryList')"
>
<view class="title">{{ item.title }}</view>
</unitv-item>
</unitv-zone>
</view>
<view class="block">
<view class="main_title">专区</view>
<unitv-zone
id="course_classification_fourth_zone"
class="search_zone"
up="course_classification_third_zone"
left="course_classification_first_zone"
down="course_classification_fifth_zone"
:values="specialZoneList"
:column="specialZoneList.length"
>
<unitv-item
v-for="(item, index) in specialZoneList"
:key="index"
:item="index"
class="item"
@click="fnClickSearch($event, 'specialZoneList')"
>
<view class="title">{{ item.title }}</view>
</unitv-item>
</unitv-zone>
</view>
</view>
<unitv-zone
id="course_classification_fifth_zone"
class="course_classification_fifth_zone"
left="course_classification_first_zone"
up="course_classification_fourth_zone"
:values="list"
:column="4"
@scrolltolower="fnScrollToLower"
>
<unitv-item
v-for="(item, index) in list"
:key="index"
:item="index"
class="item"
@click="fnCurriculumDetails"
>
<image :src="item.img" mode="widthFix" />
<view class="title line-1">{{ item.title }}</view>
</unitv-item>
</unitv-zone>
</view>
</view>
</template>
<script>
export default {
data() {
return {
systemList: [
{ title: "安全培训" },
{ title: "职业健康" },
{ title: "工伤预防" },
{ title: "职业技能" },
{ title: "建筑施工" },
{ title: "特种设备" },
{ title: "道路运输" },
{ title: "专项培训" },
],
newestList: [{ title: "最新上线" }, { title: "最多播放" }],
industryList: [
{ title: "高危行业" },
{ title: "金属冶炼" },
{ title: "煤矿开采" },
{ title: "高危行业" },
{ title: "金属冶炼" },
{ title: "煤矿开采" },
{ title: "高危行业" },
{ title: "高危行业" },
{ title: "金属冶炼" },
{ title: "煤矿开采" },
{ title: "高危行业" },
{ title: "金属冶炼" },
{ title: "煤矿开采" },
{ title: "高危行业" },
],
specialZoneList: [{ title: "免费视频" }, { title: "付费视频" }],
list: [],
// loading: true,
pagination: {
currentPage: 1,
pageSize: 10,
total: 10,
},
};
},
created() {
this.fnGetData();
},
methods: {
async fnGetData() {
// setTimeout(() => {
this.list.push({
img: require(`../../static/public_images/video_bg1.jpg`),
title: "气体基础知识",
});
this.list.push({
img: require(`../../static/public_images/video_bg2.jpg`),
title: "现场一线员工全面安全培训",
});
this.list.push({
img: require(`../../static/public_images/video_bg3.jpg`),
title: "出差人员基础安全知识与应急处置",
});
this.list.push({
img: require(`../../static/public_images/video_bg4.jpg`),
title: "现场安全负责人全面安全培训",
});
this.list.push({
img: require(`../../static/public_images/video_bg5.jpg`),
title: "《四川省安全生产条例》特色解读",
});
this.list.push({
img: require(`../../static/public_images/video_bg6.jpg`),
title: "企业贯彻落实《四川省安全生产条例》精要",
});
this.list.push({
img: require(`../../static/public_images/video_bg7.jpg`),
title: "《四川省安全生产举报奖励办法》解读",
});
this.list.push({
img: require(`../../static/public_images/video_bg8.jpg`),
title: "应急管理部关于进一步加强安全生产举报工作的指导意见",
});
this.list.push({
img: require(`../../static/public_images/video_bg9.jpg`),
title: "危险货物运输包装-气瓶",
});
this.list.push({
img: require(`../../static/public_images/video_bg10.jpg`),
title: "职业病危害专项治理",
});
// this.loading = false;
// }, 5000);
},
fnScrollToLower() {
this.pagination.currentPage++;
if (this.pagination.currentPage <= this.pagination.total)
this.fnGetData();
},
fnClickSystem(event) {
console.log(this.systemList[event].title);
this.list = [];
this.pagination = {
currentPage: 1,
pageSize: 10,
total: 10,
};
this.fnGetData();
},
fnClickSearch(event, key) {
console.log(this[key][event].title);
this.list = [];
this.pagination = {
currentPage: 1,
pageSize: 10,
total: 10,
};
this.fnGetData();
},
fnCurriculumDetails() {
uni.navigateTo({
url: "/pages/curriculum/details",
});
},
},
};
</script>
<style scoped lang="scss">
.content {
margin-top: 19rpx;
display: flex;
.system {
width: 94rpx;
.course_classification_first_zone {
font-size: 14rpx;
text-align: center;
.item {
margin-top: 5rpx;
background-color: transparent;
border-radius: 34rpx;
padding: 6rpx 18rpx;
&.hover {
background-color: #254eff;
}
&.active {
background-color: rgba(37, 78, 255, 0.39);
}
}
}
}
.right {
margin-left: 34rpx;
flex: 1;
.search {
.block {
display: flex;
margin-top: 5rpx;
&:first-child {
margin-top: 0;
}
.main_title {
font-size: 10rpx;
font-weight: bold;
margin-right: 15rpx;
padding: 2rpx 0;
margin-top: 5rpx;
}
.search_zone {
flex: 1;
display: flex;
flex-wrap: wrap;
.item {
background-color: transparent;
border-radius: 34rpx;
padding: 2rpx 12rpx;
margin-top: 5rpx;
.title {
font-size: 9rpx;
}
&.hover {
background-color: #254eff;
}
&.active {
background-color: rgba(37, 78, 255, 0.39);
}
}
}
}
}
.course_classification_fifth_zone {
margin-top: 14rpx;
display: flex;
flex-wrap: wrap;
.item {
width: 130rpx;
position: relative;
border: 2px solid transparent;
border-radius: 4rpx;
font-size: 0;
height: 117rpx;
margin-top: 14rpx;
margin-left: 10rpx;
&:not(:nth-child(n + 5)) {
margin-top: 0;
}
&:nth-child(4n + 1) {
margin-left: 0;
}
image {
width: 100%;
height: 100% !important;
border-radius: 4rpx;
}
.title {
width: 80%;
font-size: 12rpx;
color: #fff;
position: absolute;
bottom: 9rpx;
left: 7rpx;
}
&.hover {
border: 2rpx solid rgba(0, 180, 255, 0.79);
}
}
}
}
}
</style>

232
pages/index/home.vue Normal file
View File

@ -0,0 +1,232 @@
<template>
<view>
<!-- <app-loading v-if="loading" />-->
<view class="top">
<unitv-zone
id="home_first_zone"
class="home_first_zone"
up="tabs_zone"
down="home_third_zone"
:values="list.slice(0, 2)"
:column="list.slice(0, 2).length"
>
<unitv-item
v-for="(item, index) in list.slice(0, 2)"
:key="index"
:item="index"
class="item"
@click="fnNavigate('/pages/curriculum/details')"
>
<image :src="item.img" mode="widthFix" />
<view class="title line-1">{{ item.title }}</view>
</unitv-item>
</unitv-zone>
</view>
<view class="bottom">
<view class="left">
<unitv-zone
id="home_third_zone"
class="home_third_zone"
up="home_first_zone"
right="home_fourth_zone"
:values="[0, 1]"
:column="1"
:row="2"
>
<unitv-item
:item="0"
class="item"
@click="fnNavigate('/pages/mine/curriculum/index?tabIndex=1')"
>
<image src="/static/xuexi.png" mode="widthFix" />
</unitv-item>
<unitv-item
:item="1"
class="item"
@click="fnNavigate('/pages/mine/curriculum/index?tabIndex=2')"
>
<image src="/static/examination.png" mode="widthFix" />
</unitv-item>
</unitv-zone>
</view>
<view class="right">
<unitv-zone
id="home_fourth_zone"
class="home_fourth_zone"
up="home_first_zone"
left="home_third_zone"
:values="list.slice(2)"
:column="list.slice(2).length"
>
<unitv-item
v-for="(item, index) in list.slice(2)"
:key="index"
:item="index"
class="item"
@click="fnNavigate('/pages/curriculum/details')"
>
<image :src="item.img" mode="widthFix" />
<view class="title line-1">{{ item.title }}</view>
</unitv-item>
</unitv-zone>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// loading: true,
list: [],
};
},
created() {
this.fnGetData();
},
methods: {
async fnGetData() {
// setTimeout(() => {
this.list.push({
img: require(`../../static/public_images/video_bg1.jpg`),
title: "气体基础知识",
});
this.list.push({
img: require(`../../static/public_images/video_bg2.jpg`),
title: "现场一线员工全面安全培训",
});
this.list.push({
img: require(`../../static/public_images/video_bg3.jpg`),
title: "出差人员基础安全知识与应急处置",
});
this.list.push({
img: require(`../../static/public_images/video_bg4.jpg`),
title: "现场安全负责人全面安全培训",
});
this.list.push({
img: require(`../../static/public_images/video_bg5.jpg`),
title: "《四川省安全生产条例》特色解读",
});
// this.loading = false;
// }, 5000);
},
fnNavigate(url) {
uni.navigateTo({ url });
},
},
};
</script>
<style scoped lang="scss">
.top {
padding-top: 19rpx;
.home_first_zone {
display: flex;
justify-content: space-between;
.item {
width: 335rpx;
position: relative;
border: 2px solid transparent;
border-radius: 4rpx;
font-size: 0;
height: 169rpx;
image {
width: 100%;
height: 100% !important;
border-radius: 4rpx;
}
.title {
width: 80%;
font-size: 12rpx;
color: #fff;
position: absolute;
bottom: 9rpx;
left: 7rpx;
}
&.hover {
border: 2rpx solid rgba(0, 180, 255, 0.79);
}
}
}
}
.bottom {
margin-top: 10rpx;
color: #fff;
font-size: 12rpx;
display: flex;
.left {
width: 175rpx;
.home_third_zone {
.item {
margin-top: 10rpx;
width: 100%;
border-radius: 4rpx;
height: 57rpx;
text-align: center;
border: 2rpx solid transparent;
&:first-child {
margin-top: 0;
}
image {
width: 100%;
height: 100% !important;
border-radius: 4rpx;
}
&.hover {
border: 2rpx solid rgba(0, 180, 255, 0.79);
}
}
}
}
.right {
margin-left: 11rpx;
flex: 1;
.home_fourth_zone {
display: flex;
justify-content: space-between;
.item {
width: 159rpx;
position: relative;
border: 2px solid transparent;
border-radius: 4rpx;
font-size: 0;
height: 117rpx;
image {
width: 100%;
height: 100% !important;
border-radius: 4rpx;
}
.title {
width: 80%;
font-size: 12rpx;
color: #fff;
position: absolute;
bottom: 9rpx;
left: 7rpx;
}
&.hover {
border: 2rpx solid rgba(0, 180, 255, 0.79);
}
}
}
}
}
</style>

121
pages/index/index.vue Normal file
View File

@ -0,0 +1,121 @@
<template>
<view class="container">
<unitv-page
id="indexPage"
ref="indexPage"
class="indexPage"
:show="true"
@back="pageBack"
>
<app-header />
<unitv-zone
id="tabs_zone"
class="tabs_zone"
:auto-fous="true"
up="header_zone"
:down="tabsZoneDown"
:item="0"
:values="tabs"
:column="tabs.length"
>
<unitv-item
v-for="(item, index) in tabs"
:key="index"
:item="index"
class="item"
@hover="fnHoverTabs"
>
<view>{{ item.name }}</view>
</unitv-item>
</unitv-zone>
<home v-if="currentTab === 0" />
<premium-recommendation v-if="currentTab === 1" />
<course-classification v-if="currentTab === 2" />
</unitv-page>
</view>
</template>
<script>
import Home from "./home.vue";
import PremiumRecommendation from "./premium_recommendation.vue";
import CourseClassification from "./course_classification.vue";
export default {
components: {
Home,
PremiumRecommendation,
CourseClassification,
},
data() {
return {
tabs: [{ name: "首页" }, { name: "精品推荐" }, { name: "课程分类" }],
currentTab: 0,
};
},
onShow() {
if (this.$refs.indexPage) {
this.$refs.indexPage.showPage();
}
},
computed: {
tabsZoneDown() {
if (this.currentTab === 0) return "home_first_zone";
if (this.currentTab === 1) return "premium_recommendation_first_zone";
if (this.currentTab === 2) return "course_classification_first_zone";
return "";
},
},
methods: {
pageBack() {
uni.showModal({
title: "提示",
content: "是否退出",
success: function (res) {
if (res.confirm) {
plus.runtime.quit();
}
},
});
return false;
},
fnHoverTabs(item) {
this.currentTab = item;
},
},
};
</script>
<style scoped lang="scss">
.container {
.indexPage {
.tabs_zone {
font-size: 14rpx;
display: flex;
align-items: center;
margin-top: 20rpx;
text-align: center;
position: relative;
z-index: 99;
.item {
background-color: transparent;
border-radius: 34rpx;
padding: 6rpx 18rpx;
margin-left: 20rpx;
&:first-child {
margin-left: 0;
}
&.hover {
background-color: #254eff;
}
&.active {
background-color: rgba(37, 78, 255, 0.39);
}
}
}
}
}
</style>

View File

@ -0,0 +1,257 @@
<template>
<view>
<!-- <app-loading v-if="loading" />-->
<view class="top">
<view class="left">
<unitv-zone
id="premium_recommendation_first_zone"
class="premium_recommendation_first_zone"
up="tabs_zone"
right="premium_recommendation_second_zone"
down="premium_recommendation_third_zone"
:values="list.slice(0, 2)"
:column="list.slice(0, 2).length"
>
<unitv-item
v-for="(item, index) in list.slice(0, 2)"
:key="index"
:item="index"
class="item"
@click="fnCurriculumDetails"
>
<image :src="item.img" mode="widthFix" />
<view class="title line-1">{{ item.title }}</view>
</unitv-item>
</unitv-zone>
</view>
<view class="right">
<unitv-zone
id="premium_recommendation_second_zone"
class="premium_recommendation_second_zone"
up="tabs_zone"
left="premium_recommendation_first_zone"
down="premium_recommendation_third_zone"
:values="list.slice(2, 4)"
:column="1"
:row="list.slice(2, 4).length"
>
<unitv-item
v-for="(item, index) in list.slice(2, 4)"
:key="index"
:item="index"
class="item"
@click="fnCurriculumDetails"
>
<image :src="item.img" mode="widthFix" />
<view class="title line-1">{{ item.title }}</view>
</unitv-item>
</unitv-zone>
</view>
</view>
<view class="bottom">
<unitv-zone
id="premium_recommendation_third_zone"
class="premium_recommendation_third_zone"
up="premium_recommendation_first_zone"
:values="list.slice(4)"
:column="list.slice(4).length"
>
<unitv-item
v-for="(item, index) in list.slice(4)"
:key="index"
:item="index"
class="item"
@click="fnCurriculumDetails"
>
<image :src="item.img" mode="widthFix" />
<view class="title line-1">{{ item.title }}</view>
</unitv-item>
</unitv-zone>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// loading: true,
list: [],
};
},
created() {
this.fnGetData();
},
methods: {
async fnGetData() {
// setTimeout(() => {
this.list.push({
img: require(`../../static/public_images/video_bg1.jpg`),
title: "气体基础知识",
});
this.list.push({
img: require(`../../static/public_images/video_bg2.jpg`),
title: "现场一线员工全面安全培训",
});
this.list.push({
img: require(`../../static/public_images/video_bg3.jpg`),
title: "出差人员基础安全知识与应急处置",
});
this.list.push({
img: require(`../../static/public_images/video_bg4.jpg`),
title: "现场安全负责人全面安全培训",
});
this.list.push({
img: require(`../../static/public_images/video_bg5.jpg`),
title: "《四川省安全生产条例》特色解读",
});
this.list.push({
img: require(`../../static/public_images/video_bg6.jpg`),
title: "企业贯彻落实《四川省安全生产条例》精要",
});
this.list.push({
img: require(`../../static/public_images/video_bg7.jpg`),
title: "《四川省安全生产举报奖励办法》解读",
});
this.list.push({
img: require(`../../static/public_images/video_bg8.jpg`),
title: "应急管理部关于进一步加强安全生产举报工作的指导意见",
});
// this.loading = false;
// }, 5000);
},
fnCurriculumDetails() {
uni.navigateTo({
url: "/pages/curriculum/details",
});
},
},
};
</script>
<style scoped lang="scss">
.top {
padding-top: 19rpx;
display: flex;
justify-content: space-between;
.left {
width: 510rpx;
height: 169rpx;
.premium_recommendation_first_zone {
display: flex;
justify-content: space-between;
.item {
width: 164rpx;
position: relative;
border: 2px solid transparent;
border-radius: 4rpx;
font-size: 0;
height: 169rpx;
&:first-child {
width: 335rpx;
}
image {
width: 100%;
height: 100% !important;
border-radius: 4rpx;
}
.title {
width: 80%;
font-size: 12rpx;
color: #fff;
position: absolute;
bottom: 9rpx;
left: 7rpx;
}
&.hover {
border: 2rpx solid rgba(0, 180, 255, 0.79);
}
}
}
}
.right {
margin-left: 11rpx;
flex: 1;
.premium_recommendation_second_zone {
.item {
width: 164rpx;
position: relative;
border: 2px solid transparent;
border-radius: 4rpx;
font-size: 0;
height: 81rpx;
margin-top: 7rpx;
&:first-child {
margin-top: 0;
}
image {
width: 100%;
height: 100% !important;
border-radius: 4rpx;
}
.title {
width: 80%;
font-size: 12rpx;
color: #fff;
position: absolute;
bottom: 9rpx;
left: 7rpx;
}
&.hover {
border: 2rpx solid rgba(0, 180, 255, 0.79);
}
}
}
}
}
.bottom {
margin-top: 10rpx;
.premium_recommendation_third_zone {
display: flex;
justify-content: space-between;
.item {
width: 159rpx;
position: relative;
border: 2px solid transparent;
border-radius: 4rpx;
font-size: 0;
height: 117rpx;
image {
width: 100%;
height: 100% !important;
border-radius: 4rpx;
}
.title {
width: 80%;
font-size: 12rpx;
color: #fff;
position: absolute;
bottom: 9rpx;
left: 7rpx;
}
&.hover {
border: 2rpx solid rgba(0, 180, 255, 0.79);
}
}
}
}
</style>

166
pages/login/index.vue Normal file
View File

@ -0,0 +1,166 @@
<template>
<view class="container">
<unitv-page id="loginPage" ref="loginPage" class="loginPage" :show="true">
<app-header :is-show-user-info="false" />
<view class="main_title">
<text>登录可享</text>
<text>3</text>
<text>大权益</text>
</view>
<view class="rights_and_interests">
<view class="item">
<view class="icon">
<image src="/static/1080P.png" style="width: 17rpx; height: 9rpx" />
</view>
<view class="title">尊享1080P画质</view>
</view>
<view class="item">
<view class="icon">
<image
src="/static/w_yunduan.png"
style="width: 16rpx; height: 11rpx"
/>
</view>
<view class="title">同步云端观看历史</view>
</view>
<view class="item">
<view class="icon">
<image src="/static/fuli.png" style="width: 12rpx; height: 12rpx" />
</view>
<view class="title">福利活动享不停</view>
</view>
</view>
<view class="flex">
<view class="login_methods">
<view class="item">
<view class="icon">
<image
src="/static/scan_login.png"
style="width: 33rpx; height: 33rpx"
/>
</view>
<view class="right">
<view class="title">扫码登录</view>
<view class="subtitle">使用手机扫码授权</view>
</view>
</view>
</view>
<view class="qrcode">
<view class="image">
<canvas
id="qrcode"
canvas-id="qrcode"
:style="{ width: canvasSize + 'px', height: canvasSize + 'px' }"
/>
</view>
</view>
</view>
</unitv-page>
</view>
</template>
<script>
import UQRCode from "uqrcodejs";
export default {
data() {
return {
canvasSize: uni.upx2px(170),
};
},
onShow() {
if (this.$refs.loginPage) {
this.$refs.loginPage.showPage();
}
},
onReady() {
const qr = new UQRCode();
qr.data = "qrcode_url";
qr.size = this.canvasSize;
qr.make();
qr.canvasContext = uni.createCanvasContext("qrcode");
qr.drawCanvas();
},
};
</script>
<style scoped lang="scss">
.container {
.main_title {
margin-top: 10rpx;
font-size: 30rpx;
text:nth-child(2) {
color: rgba(0, 204, 255, 1);
padding: 0 4rpx;
}
}
.rights_and_interests {
margin-top: 21rpx;
display: flex;
align-items: center;
.item {
display: flex;
align-items: center;
margin-left: 18rpx;
&:first-child {
margin-left: 0;
}
.title {
margin-left: 3rpx;
font-size: 12rpx;
}
}
}
.flex {
display: flex;
justify-content: space-between;
.login_methods {
margin-top: 27rpx;
.item {
width: 359rpx;
height: 72rpx;
background-image: url("/static/scan_login_bg.png");
background-size: 100% 100%;
background-repeat: no-repeat;
display: flex;
align-items: center;
padding-left: 12rpx;
.right {
margin-left: 14rpx;
.title {
font-size: 14rpx;
}
.subtitle {
padding-top: 9rpx;
font-size: 9rpx;
color: #ccc;
}
}
}
}
.qrcode {
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.17);
border-radius: 4px;
padding: 12rpx;
.image {
background-color: #fff;
padding: 12rpx;
}
}
}
}
</style>

View File

@ -0,0 +1,144 @@
<template>
<view>
<!-- <app-loading v-if="loading" />-->
<unitv-zone
id="mine_curriculum_all_first_zone"
class="mine_curriculum_all_first_zone"
left="tabs_zone"
:values="list"
:column="4"
@scrolltolower="fnScrollToLower"
>
<unitv-item
v-for="(item, index) in list"
:key="index"
:item="index"
class="item"
@click="fnCurriculumDetails"
>
<image :src="item.img" mode="widthFix" />
<view class="title line-1">{{ item.title }}</view>
</unitv-item>
</unitv-zone>
</view>
</template>
<script>
export default {
data() {
return {
list: [],
pagination: {
currentPage: 1,
pageSize: 10,
total: 10,
},
};
},
created() {
this.fnGetData();
},
methods: {
async fnGetData() {
// setTimeout(() => {
this.list.push({
img: require(`../../../static/public_images/video_bg1.jpg`),
title: "气体基础知识",
});
this.list.push({
img: require(`../../../static/public_images/video_bg2.jpg`),
title: "现场一线员工全面安全培训",
});
this.list.push({
img: require(`../../../static/public_images/video_bg3.jpg`),
title: "出差人员基础安全知识与应急处置",
});
this.list.push({
img: require(`../../../static/public_images/video_bg4.jpg`),
title: "现场安全负责人全面安全培训",
});
this.list.push({
img: require(`../../../static/public_images/video_bg5.jpg`),
title: "《四川省安全生产条例》特色解读",
});
this.list.push({
img: require(`../../../static/public_images/video_bg6.jpg`),
title: "企业贯彻落实《四川省安全生产条例》精要",
});
this.list.push({
img: require(`../../../static/public_images/video_bg7.jpg`),
title: "《四川省安全生产举报奖励办法》解读",
});
this.list.push({
img: require(`../../../static/public_images/video_bg8.jpg`),
title: "应急管理部关于进一步加强安全生产举报工作的指导意见",
});
this.list.push({
img: require(`../../../static/public_images/video_bg9.jpg`),
title: "危险货物运输包装-气瓶",
});
this.list.push({
img: require(`../../../static/public_images/video_bg10.jpg`),
title: "职业病危害专项治理",
});
// this.loading = false;
// }, 5000);
},
fnScrollToLower() {
this.pagination.currentPage++;
if (this.pagination.currentPage <= this.pagination.total)
this.fnGetData();
},
fnCurriculumDetails() {
uni.navigateTo({
url: "/pages/curriculum/details",
});
},
},
};
</script>
<style scoped lang="scss">
.mine_curriculum_all_first_zone {
display: flex;
flex-wrap: wrap;
.item {
width: 130rpx;
position: relative;
border: 2px solid transparent;
border-radius: 4rpx;
font-size: 0;
height: 117rpx;
margin-top: 14rpx;
margin-left: 10rpx;
&:not(:nth-child(n + 5)) {
margin-top: 0;
}
&:nth-child(4n + 1) {
margin-left: 0;
}
image {
width: 100%;
height: 100% !important;
border-radius: 4rpx;
}
.title {
width: 80%;
font-size: 12rpx;
color: #fff;
position: absolute;
bottom: 9rpx;
left: 7rpx;
}
&.hover {
border: 2rpx solid rgba(0, 180, 255, 0.79);
}
}
}
</style>

View File

@ -0,0 +1,184 @@
<template>
<view>
<!-- <app-loading v-if="loading" />-->
<unitv-zone
id="mine_curriculum_completed_learning_first_zone"
class="mine_curriculum_completed_learning_first_zone"
left="tabs_zone"
:values="list"
:column="1"
:row="list.length"
@scrolltolower="fnScrollToLower"
>
<unitv-item
v-for="(item, index) in list"
:key="index"
:item="index"
class="item"
@click="fnCurriculumDetails"
>
<view class="image">
<image :src="item.img" mode="widthFix" />
</view>
<view class="right">
<view class="info">
<view class="title line-1">{{ item.title }}</view>
<view class="button">
<button>培训考核</button>
</view>
</view>
</view>
</unitv-item>
</unitv-zone>
</view>
</template>
<script>
export default {
data() {
return {
list: [],
pagination: {
currentPage: 1,
pageSize: 10,
total: 10,
},
};
},
created() {
this.fnGetData();
},
methods: {
async fnGetData() {
// setTimeout(() => {
this.list.push({
img: require(`../../../static/public_images/video_bg1.jpg`),
title: "气体基础知识",
});
this.list.push({
img: require(`../../../static/public_images/video_bg2.jpg`),
title: "现场一线员工全面安全培训",
});
this.list.push({
img: require(`../../../static/public_images/video_bg3.jpg`),
title: "出差人员基础安全知识与应急处置",
});
this.list.push({
img: require(`../../../static/public_images/video_bg4.jpg`),
title: "现场安全负责人全面安全培训",
});
this.list.push({
img: require(`../../../static/public_images/video_bg5.jpg`),
title: "《四川省安全生产条例》特色解读",
});
this.list.push({
img: require(`../../../static/public_images/video_bg6.jpg`),
title: "企业贯彻落实《四川省安全生产条例》精要",
});
this.list.push({
img: require(`../../../static/public_images/video_bg7.jpg`),
title: "《四川省安全生产举报奖励办法》解读",
});
this.list.push({
img: require(`../../../static/public_images/video_bg8.jpg`),
title: "应急管理部关于进一步加强安全生产举报工作的指导意见",
});
this.list.push({
img: require(`../../../static/public_images/video_bg9.jpg`),
title: "危险货物运输包装-气瓶",
});
this.list.push({
img: require(`../../../static/public_images/video_bg10.jpg`),
title: "职业病危害专项治理",
});
// this.loading = false;
// }, 5000);
},
fnScrollToLower() {
this.pagination.currentPage++;
if (this.pagination.currentPage <= this.pagination.total)
this.fnGetData();
},
fnCurriculumDetails() {
uni.navigateTo({
url: "/pages/curriculum/details",
});
},
},
};
</script>
<style scoped lang="scss">
.mine_curriculum_completed_learning_first_zone {
.item {
margin-top: 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
&:first-child {
margin-top: 0;
}
.image {
width: 130rpx;
height: 70rpx;
border-radius: 6rpx;
image {
border-radius: 6rpx;
width: 100%;
height: 100% !important;
}
}
.right {
flex: 1;
margin-left: 20rpx;
height: 70rpx;
display: flex;
align-items: center;
justify-content: space-between;
.info {
height: 100%;
padding: 10rpx 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.title {
width: 300rpx;
font-size: 14rpx;
}
.button {
button {
width: 70rpx;
height: 24rpx;
line-height: 24rpx;
background-color: rgba(37, 78, 255, 0.4);
border-radius: 30rpx;
color: #fff;
font-size: 9rpx;
margin: 0;
}
}
}
}
&.hover {
.right {
.info {
.button {
button {
background-color: #254eff;
}
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,113 @@
<template>
<view class="container">
<unitv-page
id="mineCurriculumPage"
ref="mineCurriculumPage"
class="mineCurriculumPage"
:show="true"
>
<app-header />
<view class="content">
<view class="tabs">
<unitv-zone
id="tabs_zone"
class="tabs_zone"
:auto-fous="true"
:right="tabsZoneRight"
:item="currentTab"
:values="tabs"
:column="1"
:row="tabs.length"
>
<unitv-item
v-for="(item, index) in tabs"
:key="index"
:item="index"
class="item"
@hover="fnHoverTabs"
>
<view class="title">{{ item.name }}</view>
</unitv-item>
</unitv-zone>
</view>
<view style="flex: 1; margin-left: 33rpx">
<all v-if="currentTab === 0" />
<learning v-if="currentTab === 1" />
<completed-learning v-if="currentTab === 2" />
</view>
</view>
</unitv-page>
</view>
</template>
<script>
import All from "./all.vue";
import Learning from "./learning.vue";
import CompletedLearning from "./completed_learning.vue";
export default {
components: {
All,
Learning,
CompletedLearning,
},
data() {
return {
tabs: [{ name: "全部" }, { name: "学习中" }, { name: "已学完" }],
currentTab: 0,
};
},
onLoad(query) {
this.currentTab = query.tabIndex ? +query.tabIndex : 0;
},
onShow() {
if (this.$refs.mineCurriculumPage) {
this.$refs.mineCurriculumPage.showPage();
}
},
computed: {
tabsZoneRight() {
if (this.currentTab === 0) return "mine_curriculum_all_first_zone";
if (this.currentTab === 1) return "mine_curriculum_learning_first_zone";
if (this.currentTab === 2)
return "mine_curriculum_completed_learning_first_zone";
return "";
},
},
methods: {
fnHoverTabs(item) {
this.currentTab = item;
},
},
};
</script>
<style scoped lang="scss">
.content {
margin-top: 19rpx;
display: flex;
.tabs {
width: 94rpx;
.tabs_zone {
font-size: 14rpx;
text-align: center;
.item {
margin-top: 5rpx;
background-color: transparent;
border-radius: 34rpx;
padding: 6rpx 18rpx;
&.hover {
background-color: #254eff;
}
&.active {
background-color: rgba(37, 78, 255, 0.39);
}
}
}
}
}
</style>

View File

@ -0,0 +1,189 @@
<template>
<view>
<!-- <app-loading v-if="loading" />-->
<unitv-zone
id="mine_curriculum_learning_first_zone"
class="mine_curriculum_learning_first_zone"
left="tabs_zone"
:values="list"
:column="1"
:row="list.length"
@scrolltolower="fnScrollToLower"
>
<unitv-item
v-for="(item, index) in list"
:key="index"
:item="index"
class="item"
@click="fnCurriculumDetails"
>
<view class="image">
<image :src="item.img" mode="widthFix" />
</view>
<view class="right">
<view class="info">
<view class="title line-2">{{ item.title }}</view>
<view class="progressbar">
<liu-progressbar
:text-hide="false"
:progress="70"
ds-color="#24408b"
bg-color="#254eff"
height="4rpx"
/>
</view>
</view>
<view class="button">
<button>继续学习</button>
</view>
</view>
</unitv-item>
</unitv-zone>
</view>
</template>
<script>
export default {
data() {
return {
list: [],
pagination: {
currentPage: 1,
pageSize: 10,
total: 10,
},
};
},
created() {
this.fnGetData();
},
methods: {
async fnGetData() {
// setTimeout(() => {
this.list.push({
img: require(`../../../static/public_images/video_bg1.jpg`),
title: "气体基础知识",
});
this.list.push({
img: require(`../../../static/public_images/video_bg2.jpg`),
title: "现场一线员工全面安全培训",
});
this.list.push({
img: require(`../../../static/public_images/video_bg3.jpg`),
title: "出差人员基础安全知识与应急处置",
});
this.list.push({
img: require(`../../../static/public_images/video_bg4.jpg`),
title: "现场安全负责人全面安全培训",
});
this.list.push({
img: require(`../../../static/public_images/video_bg5.jpg`),
title: "《四川省安全生产条例》特色解读",
});
this.list.push({
img: require(`../../../static/public_images/video_bg6.jpg`),
title: "企业贯彻落实《四川省安全生产条例》精要",
});
this.list.push({
img: require(`../../../static/public_images/video_bg7.jpg`),
title: "《四川省安全生产举报奖励办法》解读",
});
this.list.push({
img: require(`../../../static/public_images/video_bg8.jpg`),
title: "应急管理部关于进一步加强安全生产举报工作的指导意见",
});
this.list.push({
img: require(`../../../static/public_images/video_bg9.jpg`),
title: "危险货物运输包装-气瓶",
});
this.list.push({
img: require(`../../../static/public_images/video_bg10.jpg`),
title: "职业病危害专项治理",
});
// this.loading = false;
// }, 5000);
},
fnScrollToLower() {
this.pagination.currentPage++;
if (this.pagination.currentPage <= this.pagination.total)
this.fnGetData();
},
fnCurriculumDetails() {
uni.navigateTo({
url: "/pages/curriculum/details",
});
},
},
};
</script>
<style scoped lang="scss">
.mine_curriculum_learning_first_zone {
.item {
margin-top: 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
&:first-child {
margin-top: 0;
}
.image {
width: 130rpx;
height: 70rpx;
border-radius: 6rpx;
image {
border-radius: 6rpx;
width: 100%;
height: 100% !important;
}
}
.right {
flex: 1;
margin-left: 20rpx;
height: 70rpx;
display: flex;
align-items: center;
justify-content: space-between;
.info {
height: 100%;
padding: 10rpx 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.title {
font-size: 14rpx;
}
}
.button {
margin-left: 20rpx;
button {
width: 70rpx;
height: 24rpx;
line-height: 24rpx;
background-color: rgba(37, 78, 255, 0.4);
border-radius: 30rpx;
color: #fff;
font-size: 9rpx;
}
}
}
&.hover {
.button {
button {
background-color: #254eff;
}
}
}
}
}
</style>

179
pages/mine/index.vue Normal file
View File

@ -0,0 +1,179 @@
<template>
<view class="container">
<unitv-page id="minePage" ref="minePage" class="minePage" :show="true">
<app-header />
<view class="content">
<view class="info">
<view class="user_info">
<view class="avatar">
<image src="/static/avatar.png" />
</view>
<view class="username">呀哈哈</view>
</view>
<view class="statistics">
<view class="item">
<view class="title">累计开班数量</view>
<view>
<text class="value">123</text>
<text class="label"></text>
</view>
</view>
<view class="item">
<view class="title">累计学员数量</view>
<view>
<text class="value">456</text>
<text class="label"></text>
</view>
</view>
<view class="item">
<view class="title">累计培训课时</view>
<view>
<text class="value">789</text>
<text class="label"></text>
</view>
</view>
</view>
</view>
<unitv-zone
id="mine_first_zone"
class="mine_first_zone"
:values="[0, 1, 2, 3]"
:column="4"
auto-fous
:item="0"
>
<unitv-item
:item="0"
class="item"
@click="fnNavigate('/pages/mine/curriculum/index')"
>
<view class="text">我的课程</view>
</unitv-item>
<unitv-item :item="1" class="item">
<view class="text">学员档案</view>
</unitv-item>
<unitv-item :item="2" class="item">
<view class="text">效果评估报告</view>
</unitv-item>
<unitv-item :item="3" class="item">
<view class="text">定制课申请</view>
</unitv-item>
<unitv-item :item="4" class="item">
<view class="text">问题反馈</view>
</unitv-item>
</unitv-zone>
</view>
</unitv-page>
</view>
</template>
<script>
export default {
onShow() {
if (this.$refs.minePage) {
this.$refs.minePage.showPage();
}
},
methods: {
fnNavigate(url) {
uni.navigateTo({ url });
},
},
};
</script>
<style scoped lang="scss">
.content {
margin-top: 40rpx;
.info {
display: flex;
align-items: center;
justify-content: space-between;
.user_info {
display: flex;
.avatar {
border-radius: 50%;
image {
width: 71rpx;
height: 71rpx;
border-radius: 50%;
}
}
.username {
padding-top: 20rpx;
padding-left: 17rpx;
font-size: 20rpx;
font-weight: bold;
}
}
.statistics {
display: flex;
.item {
background-image: url("/static/statistics_bg.png");
background-size: 100% 100%;
background-repeat: no-repeat;
width: 127rpx;
height: 66rpx;
padding: 13rpx 14rpx;
margin-left: 9rpx;
&:first-child {
margin-left: 0;
}
.title {
font-size: 11rpx;
padding-bottom: 5rpx;
}
.value {
font-size: 16rpx;
font-weight: bold;
}
.label {
padding-left: 5rpx;
font-size: 9rpx;
}
}
}
}
.mine_first_zone {
margin-top: 40rpx;
display: flex;
justify-content: space-between;
.item {
padding: 15rpx 11rpx;
background-size: 100% 100%;
background-repeat: no-repeat;
width: 131rpx;
height: 65rpx;
border: 1rpx solid transparent;
border-radius: 4rpx;
@for $i from 1 through 5 {
&:nth-child(#{$i}) {
background-image: url("/static/mine_zone#{$i}.png");
}
}
.text {
font-size: 14rpx;
}
&.hover {
border: 1rpx solid rgba(0, 180, 255, 0.79);
}
}
}
}
</style>

BIN
static/1080P.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
static/app_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

BIN
static/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
static/bg.9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

BIN
static/bofang.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

BIN
static/buy_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/buy_success.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
static/by_shop_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
static/by_shop_money_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
static/examination.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
static/fuli.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
static/loading1000.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
static/loading_1920.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 KiB

BIN
static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
static/mine_zone1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
static/mine_zone2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
static/mine_zone3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
static/mine_zone4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
static/mine_zone5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 888 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 KiB

BIN
static/scan_login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
static/scan_login_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
static/statistics_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
static/w_yunduan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

BIN
static/xuexi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
static/zhanghao_login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

30
store/index.js Normal file
View File

@ -0,0 +1,30 @@
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
currentPage: null,
currentZone: null,
allPages: [],
},
mutations: {
pushPage(state, page) {
state.allPages[page.id] = page;
},
switchZone(state, zone) {
state.currentZone = zone;
},
switchPage(state, page) {
state.currentPage = page;
},
showPage(state, pageId) {
const page = state.allPages[pageId];
page.showPage();
},
},
actions: {},
});
export default store;

30
styles/common.scss Normal file
View File

@ -0,0 +1,30 @@
// 5
// 使1使flexmin-width: 0;
@for $i from 1 through 5 {
.line-#{$i} {
@if $i == 1 {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} @else {
display: -webkit-box !important;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: $i;
-webkit-box-orient: vertical !important;
}
}
}
* {
box-sizing: border-box;
}
.container{
width: 100vw;
min-height: 100vh;
background-image: linear-gradient(to bottom, #050114, #133682);
padding: 18rpx 33rpx;
color: #fff;
}

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-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
$uni-color-theme: #ec2224;
/* 文字基本颜色 */
$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:16;
/* 图片尺寸 */
$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;

View File

@ -0,0 +1,8 @@
## 1.0.32023-06-10
增加插件预览二维码
## 1.0.22023-04-14
增加示例
## 1.0.12023-03-30
优化功能
## 1.0.02023-03-14
初版发布

View File

@ -0,0 +1,131 @@
<template>
<view>
<view class="progress-bar" :style="{height: height,backgroundColor:dsColor}" v-if="textInside">
<view class="progress" :style="{width: progressWidth, backgroundColor: bgColor,borderRadius: borderRadius}">
</view>
<span v-if="textHide" class="percentage" :style="{color:color,fontSize:fontSize }">{{ percentage }}%</span>
</view>
<view class="card-item" v-else>
<view class="progress-bar1" :style="{height: height,backgroundColor:dsColor}">
<view class="progress1"
:style="{width: progressWidth, backgroundColor: bgColor,borderRadius: borderRadius}">
</view>
</view>
<view v-if="textHide" class="percentage1" :style="{color:color,fontSize:fontSize }">{{ percentage }}%</view>
</view>
</view>
</template>
<script>
export default {
props: {
//
textInside: {
type: Boolean,
default: true
},
//
textHide: {
type: Boolean,
default: true
},
//
progress: {
type: Number,
required: true,
validator: (value) => value >= 0 && value <= 100,
default: 50
},
//
color: {
type: String,
default: '#FFFFFF'
},
//
fontSize: {
type: String,
default: '24rpx'
},
//
bgColor: {
type: String,
default: '#5cb85c'
},
//
dsColor: {
type: String,
default: '#f2f2f2'
},
//
height: {
type: String,
default: '28rpx'
},
//
borderRadius: {
type: String,
default: '8rpx'
}
},
computed: {
progressWidth() {
return `${this.progress}%`
},
percentage() {
return Math.round(this.progress)
}
}
};
</script>
<style scoped>
.progress-bar {
position: relative;
width: 100%;
background-color: #f2f2f2;
border-radius: 16rpx;
}
.percentage {
position: absolute;
top: 50%;
left: 50%;
color: #666;
transform: translate(-50%, -50%);
font-size: 24rpx;
user-select: none;
z-index: 1;
}
.progress {
position: absolute;
top: 0;
left: 0;
height: 100%;
}
.card-item {
display: flex;
align-items: center;
justify-content: space-between;
}
.progress-bar1 {
position: relative;
width: 90%;
background-color: #f2f2f2;
border-radius: 16rpx;
}
.percentage1 {
color: #666;
font-size: 24rpx;
}
.progress1 {
position: absolute;
top: 0;
left: 0;
height: 100%;
}
</style>

View File

@ -0,0 +1,6 @@
### 1、本插件可免费下载使用
### 2、未经许可严禁复制本插件派生同类插件上传插件市场
### 3、未经许可严禁在插件市场恶意复制抄袭本插件进行违规获利;
### 4、对本软件的任何使用都必须遵守这些条款违反这些条款的个人或组织将面临法律追究。

View File

@ -0,0 +1,83 @@
{
"id": "liu-progressbar",
"displayName": "进度条组件",
"version": "1.0.3",
"description": "自定义进度条组件,支持设置文字内显、当前进度、文字颜色、文字大小、进度条颜色、进度条底色、进度条高度、进度条圆角弧度",
"keywords": [
"进度条",
"百分比",
"进度"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "u",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,44 @@
### liu-progressbar适用于uni-app项目的进度条组件
### 本组件目前兼容微信小程序、H5
### 本组件支持自定义,支持设置文字内显、当前进度、文字颜色、文字大小、进度条颜色、进度条底色、进度条高度、进度条圆角弧度
# --- 扫码预览、关注我们 ---
## 扫码关注公众号,查看更多插件信息,预览插件效果!
![](https://uni.ckapi.pro/uniapp/publicize.png)
### 使用方式
``` html
<view class="title">文字内显</view>
<liu-progressbar :progress="70" color="#FFFFFF" :height="'40rpx'" />
<view class="title">文字外显</view>
<liu-progressbar :textInside="false" :progress="70" color="#333333" :height="'40rpx'" />
<view class="title">自定义高度</view>
<liu-progressbar :progress="70" color="#FFFFFF" :height="'25rpx'" />
<view class="title">自定义圆角弧度</view>
<liu-progressbar :progress="70" color="#FFFFFF" :height="'40rpx'" :borderRadius="'40rpx'" />
<view class="title">自定义进度条颜色</view>
<liu-progressbar :progress="70" bgColor="red" color="#FFFFFF" :height="'40rpx'" />
<view class="title">自定义底色</view>
<liu-progressbar :progress="70" dsColor="red" color="#FFFFFF" :height="'40rpx'" />
```
### 属性说明
| 名称 | 类型 | 默认值 | 描述 |
| ----------------------------|---------------- | ---------------------- | ---------------|
| textInside | Boolean | true | 文字是否内显
| progress | Number | 50 | 当前进度
| color | String | #FFFFFF | 文字颜色
| fontSize | String | 24rpx | 文字大小
| bgColor | String | #5cb85c | 进度条颜色
| dsColor | String | #f2f2f2 | 进度条底色颜色
| height | String | 28rpx | 进度条高度
| borderRadius | String | 8rpx | 进度条圆角弧度

92
utils/request.js Normal file
View File

@ -0,0 +1,92 @@
const requestPath = "";
function post(url, data = {}) {
return new Promise((resolve, reject) => {
if (data && data.loading !== false) {
uni.showLoading({
title: "加载中",
mask: true,
});
}
uni.request({
url: requestPath + url,
data: {
...data,
},
header: {
"Content-type": "application/x-www-form-urlencoded",
},
method: "POST",
success: (res) => {
if (data && data.loading !== false) {
uni.hideLoading();
}
if (res.data.result === "success") {
resolve(res.data);
} else {
uni.showToast({
title: res.data.msg || "系统开小差了",
icon: "none",
});
if (res.data.msg && res.data.msg.indexOf("动火超期") !== -1) {
uni.navigateBack();
}
reject(res.data);
}
},
fail: (err) => {
if (data && data.loading !== false) {
uni.hideLoading();
}
uni.showToast({
title: "网络错误请重试",
icon: "none",
});
reject(err);
},
});
});
}
function upload(url, data = {}) {
return new Promise((resolve, reject) => {
if (data && data.loading !== false) {
uni.showLoading({
title: "加载中",
mask: true,
});
}
uni.uploadFile({
url: requestPath + url,
filePath: data.filePath,
name: data.name || "FFILE",
formData:
{
...data.formData,
} || {},
success: (res) => {
if (data && data.loading !== false) {
uni.hideLoading();
}
if (JSON.parse(res.data).result === "success") {
resolve(JSON.parse(res.data));
} else {
uni.showToast({
title: JSON.parse(res.data).msg || "系统开小差了",
icon: "none",
});
reject(JSON.parse(res.data));
}
},
fail: (err) => {
uni.hideLoading();
uni.showToast({
title: "网络错误请重试",
icon: "none",
});
reject(err);
},
});
});
}
export { post, upload };