first commit

This commit is contained in:
编码猿
2024-09-27 01:32:49 +08:00
commit 28ebe05a64
7399 changed files with 1174529 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
> 1%
last 2 versions

View File

@@ -0,0 +1,7 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 100

View File

@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: {
node: true,
},
extends: [
'plugin:vue/essential',
'@vue/airbnb',
'@vue/typescript',
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
},
parserOptions: {
parser: '@typescript-eslint/parser',
},
};

View File

@@ -0,0 +1 @@
95911da0-1b6e-4a62-92c8-bc597952b400

View File

@@ -0,0 +1,20 @@
### 1:安装依赖
---
```
$ npm install
or
$ cnpm install
or
$ yarn install
```
### 2:运行项目
---
1:开发阶段运行:
```
npm run serve
```

View File

@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
],
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
{
"name": "share",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "set NODE_ENV=dev && vue-cli-service serve",
"build": "set NODE_ENV=production && vue-cli-service build",
"lint": "vue-cli-service lint",
"dist": "serve dist",
"analyz": "npm_config_report=true npm run build"
},
"dependencies": {
"amfe-flexible": "^2.2.1",
"axios": "^0.19.0",
"core-js": "^3.3.2",
"vant": "^2.2.15",
"vue": "^2.6.10",
"vue-class-component": "^7.0.2",
"vue-property-decorator": "^8.3.0",
"vue-router": "^3.1.3",
"vuex": "^3.1.2",
"vuex-class": "^0.3.2",
"weixin-js-sdk": "^1.4.0-test"
},
"devDependencies": {
"@types/node": "^12.12.7",
"@vue/cli-plugin-babel": "^4.0.0",
"@vue/cli-plugin-eslint": "^4.0.0",
"@vue/cli-plugin-router": "^4.0.0",
"@vue/cli-plugin-typescript": "^4.0.0",
"@vue/cli-service": "^4.0.0",
"@vue/eslint-config-airbnb": "^4.0.0",
"@vue/eslint-config-typescript": "^4.0.0",
"autoprefixer": "^9.7.1",
"compression-webpack-plugin": "^3.0.0",
"css-loader": "^3.2.0",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"postcss-pxtorem": "^4.0.1",
"pug": "^2.0.4",
"pug-html-loader": "^1.1.5",
"pug-plain-loader": "^1.0.0",
"serve": "^11.2.0",
"style-resources-loader": "^1.2.1",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2",
"typescript": "~3.5.3",
"uglifyjs-webpack-plugin": "^1.1.1",
"vue-class-component": "^7.1.0",
"vue-loader": "^15.7.2",
"vue-server-renderer": "^2.6.10",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.10",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-node-externals": "^1.7.2"
}
}

View File

@@ -0,0 +1,17 @@
module.exports = {
plugins: {
autoprefixer: {
overrideBrowserslist: [
'Android 4.1',
'iOS 7.1',
'Chrome > 31',
'ff > 31',
'ie >= 8',
],
},
'postcss-pxtorem': {
rootValue: 37.5,
propList: ['*'],
},
},
};

View File

@@ -0,0 +1 @@
c83f0199-8cca-44cb-b71f-508e1d57696f

View File

@@ -0,0 +1 @@
670659ff-090b-48d7-9610-ad4a440c172b

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="【马上洗】有奖征集微影像,我和你之间最美的陪伴"/>
<meta name="apple-mobile-web-app-title" content="【马上洗】有奖征集微影像,我和你之间最美的陪伴">
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="#ee0a24"/>
<meta name="renderer" content="webkit">
<meta http-equiv="Cache-Control" content="no-siteapp"/>
<meta name="HandheldFriendly" content="true">
<meta name="screen-orientation" content="portrait">
<meta name="x5-orientation" content="portrait">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" href="https://cdn.bootcss.com/normalize/8.0.1/normalize.min.css">
<link rel="stylesheet" href="https://at.alicdn.com/t/font_1502078_2mwdm3o5jo.css">
<link rel="stylesheet" href="https://at.alicdn.com/t/font_1629634_epma4ej2ymh.css">
<title>【马上洗】有奖征集微影像,我和你之间最美的陪伴</title>
</head>
<body>
<noscript>
<strong>We're sorry but share doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app">
<!--vue-ssr-outlet-->
</div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -0,0 +1 @@
a77b6dcc-be08-42e0-8a04-5acf4b6bbf6d

View File

@@ -0,0 +1 @@
546a70c7-f707-4903-8e81-6bb9d04b5f2b

View File

@@ -0,0 +1,7 @@
import Component from 'vue-class-component';
Component.registerHooks([
'beforeRouteEnter',
'beforeRouteLeave',
'beforeRouteUpdate'
])

View File

@@ -0,0 +1 @@
54eeadce-2180-4bf0-b6b1-6f73e918fff9

View File

@@ -0,0 +1,122 @@
import VueRouter from 'vue-router';
// vue 插件配置
import 'amfe-flexible/index.js';
import Vant from 'vant';
import 'vant/lib/index.css';
// 自定义的非vue插件
import Service from "../service/imports";
import {Utils} from '../utils';
export class config {
/**
* 接口配置,测试环境,正式环境,具体接口
*/
public static AjaxConfig: any = {
DevUrl: 'https://msx.yishaxiyi.com',
ProdUrl: 'https://msx.yishaxiyi.com',
ApiList: {
banner: '/vote/banner', //首页banner
login: '/vote/login', //登录
index: '/vote/index', //首页信息
activityRecord: '/vote/activityRecord', //活动配置信息
signUp: '/vote/signUp', //提交报名
uploadImg: '/vote/uploadImg', //上传图片
rank: '/vote/rank', //投票排行接口
worksDetail: '/vote/worksDetail', //作品详情页
vote: '/vote/vote', //用户投票
createPoster: '/vote/createPoster', //生成宣传海报
getSignPackage: '/vote/getSignPackage', //获取签名信息
isUp: '/vote/isUp', //判断用户有没有报名
},
};
/**
* 需要控制未登录跳转的路由
*/
public static isRouter: Array<string> = ['/']
/**
* 浏览器本地缓存的前缀
*/
public static prefix: string = 'h5_'
/**
* 需要被挂载的节点
*/
public static mountElement: string = '#app';
/**
* 非Vue插件
*/
public static NotVuePlugs: Array<Object> = [
{n: '$http', f: Service},
{n: '$utils', f: Utils},
{m: '$config', f: config}
];
/**
* Vue插件
*/
public static VuePlugs: Array<any> = [VueRouter, Vant];
/**
* Vue-router 配置
*/
public static RouterConfigUrl: Object = {
base: '/luckdraw/',
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: Utils.AsyncComponentHook("index"),
meta: {
title: '【马上洗】有奖征集微影像,我和你之间最美的陪伴'
}
},
{
path: '/participate',
name: 'participate',
component: Utils.AsyncComponentHook("participate"),
meta: {
title: '参加活动'
}
},
{
path: '/ranking',
name: 'ranking',
component: Utils.AsyncComponentHook("ranking"),
meta: {
title: '排行榜'
}
},
{
path: '/info',
name: 'info',
component: Utils.AsyncComponentHook("info"),
meta: {
title: '【马上洗】有奖征集微影像,我和你之间最美的陪伴'
}
},
{
path: '/description',
name: 'description',
component: Utils.AsyncComponentHook("description"),
meta: {
title: '活动说明'
}
},
{
path: '/poster',
name: 'poster',
component: Utils.AsyncComponentHook("poster"),
meta: {
title: '【马上洗】有奖征集微影像,我和你之间最美的陪伴'
}
}
]
}
}

View File

@@ -0,0 +1 @@
ac23f893-8ed4-4c85-86d5-93dfc36571ab

View File

@@ -0,0 +1,130 @@
import {Utils} from '../utils';
import axios, {AxiosInstance} from 'axios';
import {Dialog, Toast} from 'vant';
export class Axios {
private instance: AxiosInstance;
private Utils: Utils = new Utils()
// @ts-ignore
public async constructor() {
this.instance = axios.create({
baseURL: Utils.CheckAjaxUrl(),
timeout: 3000
});
this.ResponseInterceptor();
this.RequestInterceptor();
}
/**
* Get 请求
* 请求参数请参考接口:RequestParams
* @param params
*/
public async get(params: { url: string, data: Object }): Promise<Object> {
try {
return await this.instance.get(params.url, {params: params.data})
} catch (e) {
return e.message
}
}
/**
* Post 请求
* 请求参数请参考接口:RequestParams
* @param params
*/
public async post(params: { url: string, data: Object, header?: Object }): Promise<Object> {
try {
return await this.instance.post(params.url, params.data, {
headers: Object.assign({}, params.header)
})
} catch (e) {
return e.message
}
}
/**
* Put 请求
* 请求参数请参考接口:RequestParams
* @param params
*/
public async put(params: { url: string, data: Object }): Promise<Object> {
try {
return await this.instance.put(params.url, params.data)
} catch (e) {
return e.message
}
}
/**
* delete 请求
* 请求参数请参考接口:RequestParams
* @param params
*/
public async delete(params: { url: string, data: Object }): Promise<Object> {
try {
return await this.instance.delete(params.url, {params: params.data})
} catch (e) {
return e.message
}
}
/**
* 添加响应拦截器
* @constructor
*/
public async ResponseInterceptor(): Promise<any> {
this.instance.interceptors.response.use(response => {
this.CloseLoadingData();
switch (response.data.status) {
case 1:
return response.data.data;
break;
default:
Toast(response.data.msg)
return 0
break;
}
}, function (error) {
return Promise.reject(error)
})
}
/**
* 添加请求拦截器
* @constructor
*/
public async RequestInterceptor(): Promise<any> {
this.instance.interceptors.request.use(config => {
// 添加token
Object.assign(config.headers,{
'token': this.Utils.getToken()
})
this.OpenLoadingData();
return config
}, function (error) {
return Promise.reject(error)
})
}
/**
* 显示加载进度
* @constructor
*/
private OpenLoadingData(): void {
Toast.loading({
message: '加载中...',
forbidClick: true,
loadingType: 'circular'
});
}
/**
* 清除加载进度条
*/
private CloseLoadingData(): void {
Toast.clear();
}
}

View File

@@ -0,0 +1 @@
091bb372-2ff6-4a70-b863-96472ce1a774

View File

@@ -0,0 +1,53 @@
import Vue, {VueConstructor} from 'vue';
import App from '@/App.vue';
import VueRouter from 'vue-router';
import '../class-component-hooks';
import {config} from '../config'
/**
* 初始化Vue需要的插件和构造Vue启动参数
*/
export class init {
private initVuePlugsArray: Array<any> = config.VuePlugs;
private initOtherPlugsArray: Array<Object> = config.NotVuePlugs;
protected router: any;
protected Vues: any = Vue;
public static App: VueConstructor = App;
constructor() {
this.Vues.config.productionTip = false;
this.initPlugs();
}
/**
* 初始化Vue和非Vue插件
*/
private initPlugs(): void {
// @ts-ignore
this.initOtherPlugsArray.forEach(v => this.Vues.prototype[v['n']] = new (v['f'])());
this.initVuePlugsArray.forEach(v => this.Vues.use(v));
this.InitVueRouter();
}
/**
* 初始化配置全局路由
* @constructor
*/
private InitVueRouter(): void {
this.router = new VueRouter(config.RouterConfigUrl);
// 重写路由Push
const routerPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location: any): any {
// @ts-ignore
return routerPush.call(this, location).catch((error: any) => error)
}
this.router.beforeEach((to: any, from: any, next: any) => {
if (!!to.meta.title) {
document.title = to.meta.title;
}
next();
});
}
}

View File

@@ -0,0 +1,20 @@
import {init} from './init';
import {config} from '../config';
import store from '../store/index'
/**
* Vue项目的启动文件
*/
class Run extends init {
public initApp(): void {
const router = this.router;
new this.Vues({
router,
store,
render: (h: (arg0: any) => void): any => h(init.App)
}).$mount(config.mountElement);
}
}
(new Run()).initApp();

View File

@@ -0,0 +1 @@
898e2eec-093d-4299-9222-24eb96093a9c

View File

@@ -0,0 +1,14 @@
export interface PublicService {
getBanner(): Promise<Object[]>
login(code: string): Promise<Object[]>
getIndex(data: any): Promise<Object[]>
activityRecord(): Promise<Object[]>
signUp(data: any): Promise<Object[]>
uploadImg(file: any): Promise<Object[]>
rank(page: number): Promise<Object[]>
worksDetail(id: string): Promise<Object[]>
vote(id: string): Promise<Object[]>
createPoster(url: string, id: string): Promise<Object[]>
getSignPackage(): Promise<Object[]>
isUp(): Promise<Object[]>
}

View File

@@ -0,0 +1 @@
b5e71e73-c5a6-40ed-b422-d58a62bf2755

View File

@@ -0,0 +1,120 @@
import { PublicService } from '../PublicService'
import {Axios} from "../../dao";
import {config} from "../../config";
import {Utils} from '../../utils';
const utils: Utils = new Utils()
export class PublicServiceImpl extends Axios implements PublicService {
// 首页banner图
public async getBanner(): Promise<any> {
return await this.get({
url: config.AjaxConfig.ApiList.banner,
data: {}
});
}
// 登录
public async login(code: string): Promise<any> {
return await this.get({
url: config.AjaxConfig.ApiList.login,
data: {
code: code
}
});
}
// 首页
public async getIndex(data: any): Promise<any> {
return await this.post({
url: config.AjaxConfig.ApiList.index,
data: data
});
}
// 活动配置信息
public async activityRecord(): Promise<any> {
return await this.get({
url: config.AjaxConfig.ApiList.activityRecord,
data: {}
});
}
// 提交报名
public async signUp(data: any): Promise<any> {
return await this.post({
url: config.AjaxConfig.ApiList.signUp,
data: data
});
}
// 上传图片
public async uploadImg(file: any): Promise<any> {
return await this.post({
url: config.AjaxConfig.ApiList.uploadImg,
data: file,
header: {
'Content-Type': 'multipart/form-data'
}
});
}
// 排行榜
public async rank(page: number): Promise<any> {
return await this.get({
url: config.AjaxConfig.ApiList.rank,
data: {
page: page
}
});
}
// 作品详情
public async worksDetail(id: string): Promise<any> {
return await this.get({
url: config.AjaxConfig.ApiList.worksDetail,
data: {
id: id
}
});
}
// 用户投票
public async vote(id: string): Promise<any> {
return await this.post({
url: config.AjaxConfig.ApiList.vote,
data: {
id: id
}
});
}
// 生成海报
public async createPoster(url: string, id: string): Promise<any> {
return await this.post({
url: config.AjaxConfig.ApiList.createPoster,
data: {
url: url,
id: id
}
});
}
// 获取签名信息
public async getSignPackage(): Promise<any> {
const u = navigator.userAgent, app = navigator.appVersion;
const isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); // ios终端
return await this.post({
url: config.AjaxConfig.ApiList.getSignPackage,
data: {
url: isiOS? utils.getStorage('baseUrl') : window.location.href.split('#')[0]
}
});
}
// 判断用户有没有报名
public async isUp(): Promise<any> {
return await this.post({
url: config.AjaxConfig.ApiList.isUp,
data: {}
});
}
}

View File

@@ -0,0 +1,5 @@
import {PublicServiceImpl} from "./impl/PublicServiceImpl";
export default class Imports {
public PublicService = new PublicServiceImpl()
}

View File

@@ -0,0 +1 @@
62f39136-4b07-4019-acc0-4a1c643841a8

View File

@@ -0,0 +1,7 @@
import { Commit } from 'vuex'
export default {
ActivityRecord(context: { commit: Commit }, n: Object){
context.commit('SET_ActivityRecord', n)
}
}

View File

@@ -0,0 +1,5 @@
export default {
getActivityRecord: (state:any) => {
return state.activityRecord
}
}

View File

@@ -0,0 +1,19 @@
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
Vue.use(Vuex)
const store = new Vuex.Store({
state:{
activityRecord: {}, //项目配置
},
actions,
mutations,
getters
})
export default store

View File

@@ -0,0 +1 @@
604e7698-91f2-47c7-ac7d-c693ed526f06

View File

@@ -0,0 +1,5 @@
export default {
SET_ActivityRecord (state:any, n: Object) {
state.activityRecord = n
}
}

View File

@@ -0,0 +1 @@
59ea7469-7520-4226-a2e6-6df399904e91

View File

@@ -0,0 +1,117 @@
import { config } from '../config';
import wx from 'weixin-js-sdk'
export class Utils {
/**
* 检测当前环境,动态返回ajax接口的对应的URL地址
* @constructor
*/
public static CheckAjaxUrl(): string {
if (process.env.NODE_ENV === 'production') {
return config.AjaxConfig.ProdUrl;
} else {
return config.AjaxConfig.DevUrl;
}
}
/**
* 异步加载组件,仅限 pages 中组件
* @param fileName 组件的文件名称
* @constructor
*/
public static AsyncComponentHook(fileName: String): Function {
const component = import(/* webpackChunkName: "[request]" */ `@/pages/${fileName}.vue`);
component.catch(e => console.error(`组件加载失败: ${e}`));
return () => component;
}
/**
* 动态设置网页title
* @param title
*/
public setTitle(title: string): void {
document.title = title;
}
/**
* 本地存储
* @param key 存储的名称
* @param value 存储的数据
*/
public setStorage(key: string, value: any): void {
try {
if (typeof key !== 'string') {
throw "key必须为字符串";
}
localStorage.setItem(config.prefix + key, JSON.stringify(value))
} catch (error) {
console.error(error)
}
}
/**
* 获取本地存储
* @param key 存储的名称
*/
public getStorage(key: string): any {
try {
let value: string = localStorage.getItem(config.prefix + key) as string
return JSON.parse(value)
} catch (error) {
console.error(`获取key:${error}的本地缓存数据失败`)
}
}
/**
* 删除本地存储
* @param key 存储的名称
*/
public delStorage(key: string): void {
return localStorage.removeItem(config.prefix + key);
}
/**
* 获取token
*/
public getToken(): void {
return this.getStorage('access_token')
}
/**
* id 身份证号
* 身份证验证
*/
public isIdentity(id: string): boolean{
let str: RegExp = /^[1-9]\d{5}(18|19|20|(3\d))\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
return str.test(id)
}
/**
* id 手机号
* 手机号证验证
*/
public isTel(id: string): boolean{
let str: RegExp = /^1[3456789]\d{9}$/
return str.test(id)
}
public wxConfig(data: any): void {
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: data.appId, // 必填,公众号的唯一标识
timestamp: data.timestamp , // 必填,生成签名的时间戳
nonceStr: data.nonceStr, // 必填,生成签名的随机串
signature: data.signature,// 必填,签名
jsApiList: ["chooseImage","uploadImage", "updateAppMessageShareData", "updateTimelineShareData", "onMenuShareAppMessage"] // 必填,需要使用的JS接口列表
});
}
/**使用微信sdk
* data 初始号数据
* ready 初始号成功后后续操作需要在这使用
*/
public wxSdk(ready: Function): void {
wx.ready(function(){
ready(wx)
});
}
}

View File

@@ -0,0 +1 @@
c38c3f34-2c8d-418f-99c6-f393ebe30993

View File

@@ -0,0 +1,64 @@
<template lang="pug">
#app
router-view
TabbarComponent(v-if="isTabBar")
</template>
<script lang="ts">
import {Component, Provide, Vue} from 'vue-property-decorator';
import TabbarComponent from "@/components/Tabbar.vue";
import { Action } from 'vuex-class'
@Component({
components: {
TabbarComponent
}
})
export default class App extends Vue {
$http: any
$utils: any
@Action('ActivityRecord') ActivityRecord : any
// 控制导航条是否显示
public isTabBar: boolean = true;
// 为后代组件提供方法 HiddenTabBar,ShowTabBar
@Provide() HiddenTabBar = this.hiddenTabBar
@Provide() ShowTabBar = this.showTabBar
// 关闭底部导航条
public hiddenTabBar(): void {
this.$nextTick(function () {
this.isTabBar = false
})
}
// 显示底部导航
public showTabBar(): void {
this.$nextTick(function () {
this.isTabBar = true
})
}
created() {
this.activityRecord()
const u = navigator.userAgent, app = navigator.appVersion;
const isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); // ios终端
if(isiOS){
this.$utils.setStorage('baseUrl', window.location.href.split('#')[0])
}
}
// 获取项目配置
private async activityRecord() : Promise<void> {
let res = await this.$http.PublicService.activityRecord()
document.title = res.activity_name
this.ActivityRecord(res)
}
}
</script>
<style lang="stylus">
@import "./assets/stylus/tpl/_public.styl"
</style>

View File

@@ -0,0 +1 @@
38b5ad22-f085-4384-a9f9-c6736bbd8a6b

View File

@@ -0,0 +1 @@
778f0b03-e871-43d8-8f7c-3122ed0e3be7

View File

@@ -0,0 +1,3 @@
// 对外全局导出,在vue.config.js被导入
@import "./tpl/colors.styl"
@import "./tpl/mixins.styl"

View File

@@ -0,0 +1 @@
693c63c6-6a6e-4e7b-a8a8-c4c4562f2053

View File

@@ -0,0 +1,110 @@
.index
.banner
height auto
width 100%
.cumulative
background-color #ffffff
height 1.8rem
border-radius 0.2rem
position relative
&::after
content ''
position absolute
height 1.2rem
width 0.02rem
background-color #cccccc
left 50%
top 50%
margin -0.6rem 0
.cumulative_item
text-align center
.item_p1
font-size 0.53rem
color #892f38
.item_p2
margin-top 0.20rem
font-size 0.32rem
.time
background-color #ffffff
height 2.75rem
border-radius 0.2rem
text-align center
.time_text
font-size 0.35rem
.time_html
display inline-block
font-size 0.35rem
color #666666
margin-left 0.4rem
.tiem_button
font-size 0.43rem
background-color #ad2022
width 4rem
height 1.07rem
line-height 1.07rem
margin 0.4rem auto 0
border-radius 0.1rem
color #ffffff
img
height 0.45rem
width 0.45rem
margin-right 0.1rem
a
color #ffffff
.search
background-color #ffffff
padding 0.2rem 0.2rem
border-radius 0.1rem
input
display inline-block
border 0
height 0.5rem
font-size 0.4rem
img
height 0.53rem
width 0.53rem
.cell_row
margin-top 0.15rem
border-radius 0.2rem
width 98%
display inline-block
box-sizing border-box
.row_img
position relative
img
width 100%
height auto
.number
position absolute
right 0
top 0.24rem
width 1.67rem
height 0.67rem
background-color rgba(0,0,0,.6)
text-align center
line-height 0.67rem
border-radius 0.1rem 0 0 0.1rem
color #ffffff
font-size 0.35rem
.row_text
padding 0.32rem 0.21rem
.title
.name
font-size 0.4rem
.num
font-size 0.4rem
text-align right
color rgba(172,32,33,1)
.row_button
font-size 0.43rem
background-color #ad2022
width 100%
height 1.07rem
line-height 1.07rem
color #ffffff
margin 0.2rem auto 0
border-radius 2rem
img
height 0.45rem
width 0.45rem
margin-right 0.1rem

View File

@@ -0,0 +1,105 @@
.info
padding-bottom 1.6rem
.info_img
height 1.87rem
width 1.87rem
overflow hidden
border-radius 50%
margin 0.32rem auto 0
.info_name
font-size 0.43rem
color #ffffff
text-align center
margin-top 0.32rem
.cumulative
background-color #ffffff
height 1.8rem
border-radius 0.2rem
position relative
&::after
content ''
position absolute
height 1.2rem
width 0.02rem
background-color #cccccc
left 50%
top 50%
margin -0.6rem 0
.cumulative_item
text-align center
.item_p1
font-size 0.53rem
color #892f38
.item_p2
margin-top 0.20rem
font-size 0.32rem
.works
background-color #ffffff
border-radius 0.2rem
padding 0.47rem 0.35rem
.works_title
font-size 0.48rem
font-weight 500
color #000000
text-align center
.works_text
font-size 0.4rem
color rgba(51,51,51,1)
margin-top 0.48rem
text-align center
.works_img
img
width 100%
height auto
.tabbar
position fixed
bottom 0
left 0
right 0
height 1.47rem
background-color #ffffff
font-size 0.37rem
color #333333
.tabbar_img
height 0.67rem
width auto
margin-right 0.2rem
a
font-size 0.37rem
color #333333
.piao
position relative
.vote
position absolute
top -0.6rem
height 1.6rem
width 1.6rem
border-radius 50%
overflow hidden
left 50%
margin-left -0.8rem
background-image: linear-gradient(to right, #fcb256 , #ff8f0d)
color #ffffff
font-size 0.45rem
.poster
position fixed
bottom 2rem
right .2rem
height 1.2rem
width 1.2rem
text-align center
line-height 1.2rem
background-color rgba(0,0,0,0.6)
color #ffffff
font-size 0.36rem
border-radius 50%
.poster_button
background #e6201a
margin 0.5rem auto
font-size 0.42rem
color #ffffff
width 4rem
height 1rem
line-height 1rem
text-align center
border-radius 0.1rem

View File

@@ -0,0 +1,55 @@
.participate
.toutu
height 4.93rem
width 100%
.somCell
.title
.left
font-size 0.4rem
color rgba(153,153,153,1)
.right
font-size 0.35rem
color rgba(172,32,33,1)
text-align right
.center
margin-top 0.32rem
padding: 0.26667rem 0.42667rem
.updata_img
display flex
.preview_img
position relative
margin 0 0.3rem 0.3rem 0
img
width 2.13333rem
height 2.13333rem
.preview_close
position absolute
top -0.21333rem
right -0.21333rem
color #969799
font-size 0.48rem
background-color #fff
border-radius 100%
.updata_click
width 2.13333rem
height 2.13333rem
border 0.01rem solid #cccccc
position relative
.preview_plus
color #969799
font-size 0.48rem
background-color #fff
position absolute
top 50%
left 50%
margin -0.28rem 0 0 -0.28rem
.participate_button
width 7.68rem
height 1.2rem
line-height 1.2rem
text-align center
background rgba(172,32,33,1)
border-radius 1.2rem
color #ffffff
font-size 0.52rem
margin 0.32rem auto 0

View File

@@ -0,0 +1,53 @@
.ranking
.toutu
width 100%
height auto
.ranking_list
background-color #ffffff
padding 0 0.29rem 0.26rem
border-radius 0.1rem
.title
font-weight 500
font-size 0.48rem
padding 0.6rem 0
text-align center
.ranking_row
background-color rgba(242,243,245,1)
display flex
align-items center
height 1.73rem
padding 0 0.32rem
.paihang_img
img
height 0.6rem
width 0.6rem
.paihang
height 0.64rem
width 0.64rem
text-align center
line-height 0.64rem
color #ffffff
background-color #bfbfbf
border-radius 50%
font-size 0.28rem
.ranking_img
height 1.17rem
width 1.17rem
border-radius 50%
overflow hidden
margin-left 0.29rem
.name
font-size 0.4rem
margin-left 0.24rem
color #333
.votes
font-size 0.35rem
color #666
span
color #cb4a46
font-size 0.43rem
margin-right 0.17rem
.bottomLoad
font-size 0.37rem
color #ffffff
text-align center

View File

@@ -0,0 +1 @@
46a07263-8294-4523-b692-7c69bc759ddf

View File

@@ -0,0 +1,124 @@
// 网站默认样式修改
html,body,#app
height 100%
h1,h2,h3,h4,h5,h6,ul,li,p
padding 0
margin 0
// 自定义全局css
.line
width 100%
height 0.267rem
background #f6f6f6
.content
padding-bottom 60px
.inline_block
display inline-block
.mt80
margin-top 0.8rem
.mt25
margin-top 0.25rem
.mt10
margin-top 0.1rem
.mt32
margin-top 0.32rem
.mt36
margin-top 0.36rem
.mt40
margin-top 0.4rem
.ml10
margin-left 0.1rem
.ml20
margin-left 0.2rem
.ml40
margin-left 0.4rem
.mr10
margin-right 0.1rem
.pd43
padding 0 0.43rem
.pd32
padding 0 0.32rem
.pt20
padding-top 0.2rem
.mt
margin-top mt()
// 框架的一些样式重置
// .task 限制只在task页面生效
.task .van-tab--active,
.news .van-tab--active
background $themeColor
.task .van-tabs__line,
.news .van-tabs__line
background #ee0a2400 !important
.task .van-tabs__wrap,
.news .van-tabs__wrap
height 1.17333rem
border-bottom 2px solid $themeColor
.task_info .van-cell,
.news .van-cell
padding-left 0
padding-right 0
.task_hall .van-tabs__line,
.news .van-tabs__line,
.CapitalDetails .van-tabs__line{
background-color: $themeColor;
}
// task和taskinfo页面列表头部的公共样式
// 放在这里减少冗余
.items_title
display flex
justify-content space-between
height 1.187rem
align-items center
border_bottom()
.title_status
color: red;
font-size 13px
.title_left
display flex
align-items center
img
width 0.573rem
height 0.573rem
margin-right 0.28rem
span
font-size 13px
margin-right 0.16rem
.y
padding 0.107rem 0.24rem
width 1.067rem
text-align center
color #fff
font-size 12px
display inline-block
.y_1
background #1ab51f
margin-right 0.147rem
.y_2
background #ff9c07
// 按钮统一处理
.account_sub
padding 0.267rem 0.48rem
.account_sub_top
padding 0.267rem 0
// 自定义面板组件的下划线
// .mycell
// &:not(:last-child)::after
// position: absolute;
// box-sizing: border-box;
// content: ' ';
// pointer-events: none;
// right: 0;
// bottom: 0;
// left: 0.42667rem;
// border-bottom: 0.02667rem solid #ebedf0;
// transform: scaleY(.5);

View File

@@ -0,0 +1,8 @@
// 公共颜色配置
// app 主题色
$themeColor = #20c6c2
$themeFontColor = #999
$taskColor = #1ab51f

View File

@@ -0,0 +1,22 @@
//公共css方法
.img
width 100%;
height 100%;
.flex
display flex;
.direction_column
flex-direction column;
.flex1
flex 1;
.flex_cen
display: flex;
justify-content: center;
align-items: center;
align-content: center
.pointer
cursor pointer;
border_bottom()
border-bottom: 1px solid #ebedf0;
mt(a=10)
unit(a, px)

View File

@@ -0,0 +1 @@
690c385d-77fd-4dcf-8670-3c8f3352ceb9

View File

@@ -0,0 +1,68 @@
<template lang="pug">
.Address
van-field(clearable='', :label='areaTitle', disabled right-icon='arrow', :placeholder="area !== '选择地址'? area : areaText", @click="isPopup")
//- 地址选择弹窗
van-popup(v-model='show', position='bottom', :style="{ height: '40%' }")
van-area( :value='value' :area-list="areaList" @confirm="onConfirm" @cancel="isPopup" show-toolbar)
</template>
<script lang="ts">
import {Component, Provide, Vue, Prop} from 'vue-property-decorator';
type AreaType<T> = {
[K in keyof T]: T[K]
}
interface AreaList {
[id: string]: AreaType<object>[]
}
@Component
export default class Address extends Vue {
$http: any
// 回显的区id
@Prop({default: '110101'}) readonly value!: string
// 回显的文字
@Prop({default: '选择地址'}) readonly areaText!: string
// 回显的文字
@Prop({default: '收货地址'}) readonly areaTitle!: string
// 是否禁止弹出框弹出
@Prop({default: false}) readonly isShow!: boolean
private show: Boolean = false
private area: string = '选择地址'
private areaId: number[] = []
// 省市区数据
private areaList: AreaList = {}
created() {
this.getvantcity()
}
private isPopup() {
if(this.isShow) return;
this.show = !this.show
}
// 地址数据
private async getvantcity(): Promise<void> {
let res = await this.$http.PublicService.getvantcity()
this.areaList = res
}
// 确定选择城市
private onConfirm(value: any, index: any): void {
this.area = ''
this.areaId = []
value.forEach((v: any, i: number) => {
this.area += (v.name + ' ')
this.areaId.push(v.code)
});
this.show = !this.show
this.$emit('confirm', this.areaId)
}
}
</script>
<style lang="stylus" scoped>
</style>

View File

@@ -0,0 +1,190 @@
<template lang="pug">
.image
.waterfall
.column(v-for="(item,list) in imageList" :key="list")
.column-item.cell_row(v-for="(obj,index) in item" :key="index")
div(style='background-color:#ffffff')
.row_img
img( :src='obj.image')
.number 编号: {{obj.num}}
.row_text
.title.flex
.name.flex1 {{obj.user_name}}
.num.flex1 {{obj.poll}}票
.row_button.flex_cen
img(src='image/time_button.png')
| 投票
.footer(v-show="footerVisible")
h5 我是有底线的
</template>
<script lang="ts">
import {Component, Provide, Vue, Prop, Watch} from 'vue-property-decorator';
@Component
export default class Cascade extends Vue {
@Prop({default: []}) readonly imgData!: any[]
private tempImage: any[] = []
private imageHeight: any[] = [0, 0]
private imageWidth: number = 0
private imageList: any[] = [[],[]]
private footerVisible: boolean = false
private height1: any
private height2: any
private height3: any
private available: any
private pagination: any = {
currentPage: 1,
totalPage: 10,
pageSize: 10,
available: true
}
mounted() {
let doc: any = document.querySelector(".waterfall>.column")
this.imageWidth = doc.offsetWidth
window.addEventListener("scroll", this.pullDown);
}
@Watch('imgData')
private getImgData(n: any, o: any): void {
this.tempImage = [...n]
this.pushImage(0);
}
public emptyClick(): void{
this.imageList = [[],[]]
this.imageHeight = [0, 0]
this.tempImage = []
}
private pushImage(index: number) {
if (index >= this.tempImage.length) return;
let img = new Image(),that = this;
img.src = this.tempImage[index].image;
img.onload = () => {
let min = that.imageHeight[0],imageIndex = 0;
that.imageHeight.forEach(function(item, _index) {
if (min > item) {
min = item;
imageIndex = _index;
}
});
that.imageHeight[imageIndex] += (img.naturalHeight / img.naturalWidth + 96 / this.imageWidth);
that.imageList[imageIndex].push(that.tempImage[index]);
that.pushImage(index + 1);
};
}
private pullDown(): void {
// 获取滚轮位置
this.height1 =
window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop;
// 文档高度
this.height2 = document.body.scrollHeight;
// 可视区域
// 设置compatMode兼容IE
this.height3 =
document.compatMode === "CSS1Compat"
? document.documentElement.clientHeight
: document.body.clientHeight;
// 如果滚动到最低(这里设置离最底还有100距离才触发函数)
// available条件是为了防止触底时一直不断地请求。因此,请求一次后available设为0,直到滚动到离底部超过100距离(即数据加载玩后)才设为1
if (this.height3 + this.height1 >= this.height2 - 100 && this.available) {
this.available = false;
//请求下一页,如果当前页等于最大页,直接返回
if (this.pagination.currentPage >= this.pagination.totalPage) {
this.footerVisible = true;
return;
}
this.pagination.currentPage++;
// this.getImgData();
this.$emit('load')
} else if (this.height3 + this.height1 < this.height2 - 100) {
this.available = true;
}
}
}
</script>
<style lang="stylus" scoped>
.waterfall
margin-top 10px
display flex
.column
width 50%
.column-item
width 100%
padding 0.1rem
box-sizing border-box
img
width 100%
height 100%
.date
margin-bottom 10px
.title
margin-bottom 10px
.content
margin-bottom 10px
.cell_row
margin-top 0.15rem
border-radius 0.2rem
width 98%
display inline-block
box-sizing border-box
.row_img
position relative
img
width 100%
height auto
.number
position absolute
right 0
top 0.24rem
width 1.67rem
height 0.67rem
background-color rgba(0,0,0,.6)
text-align center
line-height 0.67rem
border-radius 0.1rem 0 0 0.1rem
color #ffffff
font-size 0.35rem
.row_text
padding 0.32rem 0.21rem
.title
.name
font-size 0.4rem
.num
font-size 0.4rem
text-align right
color rgba(172,32,33,1)
.row_button
font-size 0.43rem
background-color #ad2022
width 100%
height 1.07rem
line-height 1.07rem
color #ffffff
margin 0.2rem auto 0
border-radius 2rem
img
height 0.45rem
width 0.45rem
margin-right 0.1rem
.footer
width 100%
height 80px
display flex
justify-content center
align-items center
border-top 10px
color #ffffff
margin-top 10px
</style>

View File

@@ -0,0 +1,93 @@
<template lang="pug">
.CustomTabs
van-tabs(v-model='active'
:swipe-threshold="4"
@click="onClick"
animated
title-active-color="#fff"
:border="false"
swipeable)
van-tab(v-for="(item,index) in list" :key="index")
div(slot='title')
| {{item.title}}
img(v-if="active == index" src="image/task_list_right.png").triangle
van-search(class="search" v-model='SearchKey',
placeholder='请输入刷新关键词',
right-icon="search"
left-icon=""
@search='',
@cancel='')
div.list(v-for="(it,i) in item.content" :key="i")
router-link(to="/taskInfo" tag="div" class="tabs_items")
.items_title
.title_left
img(:src="it.shop_icon" alt="")
span {{it.shop_name}}
.y_1.y 直返
.y_2.y 信誉
.title_status {{it.shop_status}}
.item_text
.text_1.t
span 订单编号:{{it.shop_number}}
span 垫付:¥{{it.shop_money}}
.text_2.t
span 剩余时间:{{it.shop_time}}
span 佣金:¥{{it.shop_commission}}
.item_action
van-icon(name='ellipsis')
.action_list
van-button(type='default' size="small") 取消
van-button(type='default' size="small") 追评
van-button(type='danger' size="small") 开始操作
.line
</template>
<script lang="ts">
import {Component, Provide, Vue, Prop} from 'vue-property-decorator';
@Component
export default class CustomTabs extends Vue {
private active: number = 0;
private SearchKey: String = "";
@Prop({ default: [] }) readonly list: Array<Object> | undefined
private onClick(name: string, title: number): void {
console.log(name,title)
}
}
</script>
<style scoped lang="stylus">
.triangle
position absolute
right -0.35rem
top -0.08rem
.tabs_items
width 100%
padding 0.373rem 0.476rem
background #fff
box-sizing border-box
.item_text
margin-bottom 0.547rem
.t
font-size 0.36rem
color #999
display flex
justify-content space-between
.text_1
margin-top 0.4rem
margin-bottom 0.253rem
.item_action
display flex
justify-content space-between
align-items center
button
margin-right 0.133rem
&:last-child
margin-right 0
</style>

View File

@@ -0,0 +1,19 @@
<template lang="pug">
i.icons( :class="'iconfont '+name" :style='`font-size:${size}rem;color:${color};`')
</template>
<script lang="ts">
import {Component, Provide, Vue, Prop} from 'vue-property-decorator';
@Component
export default class Icons extends Vue {
@Prop({default:''}) readonly name!: string
@Prop({default:'#000000'}) readonly color!: string
@Prop({default:'0.2'}) readonly size!: string
}
</script>
<style lang="stylus" scoped>
.icons
display inline-block
box-sizing border-box
line-height normal
</style>

View File

@@ -0,0 +1,145 @@
<template lang="pug">
.imgList
.flex()
.img_cell.flex1
.cell_row(v-for='(item,index) in img' :key='index' @click='goUrl(item.id)')
div(style='background-color:#ffffff')
.row_img
img( :src='item.image')
.number 编号: {{item.num}}
.row_text
.title.flex
.name.flex1 {{item.user_name}}
.num.flex1 {{item.poll}}票
.row_button.flex_cen
img(src='image/time_button.png')
| 投票
.img_cell.flex1
.cell_row(v-for='(item,index) in img2' :key='index' @click='goUrl(item.id)')
div(style='background-color:#ffffff')
.row_img
img( :src='item.image')
.number 编号: {{item.num}}
.row_text
.title.flex
.name.flex1 {{item.user_name}}
.num.flex1 {{item.poll}}票
.row_button.flex_cen
img(src='image/time_button.png')
| 投票
</template>
<script lang="ts">
import {Component, Prop, Vue, Watch} from 'vue-property-decorator';
@Component({})
export default class ImgList extends Vue {
@Prop({default: []}) imgData!: any[]
private img: any[] = []
private img2: any[] = []
created() {
}
private goUrl(id: string): void{
this.$router.push(`/info?id=${id}`)
}
private vote(id: string): void{
this.$emit('vote', id)
}
private onLoad(): void {
this.$emit('load')
}
@Watch('imgData')
private async newImgData(n: any[], o: any[]) {
this.img2 = []
this.img = []
let i1:number=0, i2:number=0;
let that = this
let newArr: any[] = []
n.forEach( async (v: any, i:number)=>{
newArr.push(v.image)
})
let res: any = await this.onloadImg(newArr)
res.forEach((v: any, i:number)=>{
if(i1 <= i2){
this.img.push(n[i])
i1 += v
}else{
this.img2.push(n[i])
i2 += v
}
})
}
// 获取图片高度
private onloadImg(newArr: any[]) {
return new Promise((resolve,reject)=>{
let arr: any[] = []
newArr.forEach((v: any, i:number)=>{
let img = new Image();
img.src = v;
img.onload = function(){
// 统一图片宽度进行同比缩放计算 统一的宽度随意
// 用于累计数组高度时 确保所有图片 宽度一致 缩放高度
arr[i] = img.height * (50/img.width)
if(arr.length == newArr.length){
resolve(arr)
}
};
})
})
}
}
</script>
<style lang="stylus" scoped>
.cell_row
margin-top 0.15rem
border-radius 0.2rem
width 98%
display inline-block
box-sizing border-box
.row_img
position relative
img
width 100%
height auto
.number
position absolute
right 0
top 0.24rem
width 1.67rem
height 0.67rem
background-color rgba(0,0,0,.6)
text-align center
line-height 0.67rem
border-radius 0.1rem 0 0 0.1rem
color #ffffff
font-size 0.35rem
.row_text
padding 0.32rem 0.21rem
.title
.name
font-size 0.4rem
.num
font-size 0.4rem
text-align right
color rgba(172,32,33,1)
.row_button
font-size 0.43rem
background-color #ad2022
width 100%
height 1.07rem
line-height 1.07rem
color #ffffff
margin 0.2rem auto 0
border-radius 2rem
img
height 0.45rem
width 0.45rem
margin-right 0.1rem
</style>

View File

@@ -0,0 +1,43 @@
<template lang="pug">
//- 面板组件
.mycell.van-cell
.cell__title
span {{title}}
.cell__value
slot
</template>
<script lang="ts">
import {Component, Prop, Vue} from 'vue-property-decorator';
@Component({})
export default class Fund extends Vue {
// 标题名称
@Prop({default:''}) protected title!:string
}
</script>
<style lang="stylus" scoped>
.mycell
position: relative;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
box-sizing: border-box;
width: 100%;
padding: 0.26667rem 0.42667rem;
overflow: hidden;
color: #323233;
font-size: 0.37333rem;
line-height: 0.64rem;
background-color: #fff;
.cell__title
flex: none;
width: 2.4rem;
.cell__value
position: relative;
overflow: hidden;
color: #969799;
vertical-align: middle;
word-wrap: break-word;
flex 1
</style>

View File

@@ -0,0 +1,28 @@
<template lang="pug">
.MyPanel
.title
span {{title}}
.center
slot
</template>
<script lang="ts">
// 面板组件
import {Component, Provide, Vue, Prop} from 'vue-property-decorator';
@Component
export default class MyPanel extends Vue {
// 标题文字
@Prop({default: '标题'}) readonly title!: string
}
</script>
<style lang="stylus" scoped>
.MyPanel
.title
padding 0.4rem 0.22667rem 0.4rem
display flex
align-items center
span
margin-left 0.2rem
font-size 0.52rem
font-weight 500
</style>

View File

@@ -0,0 +1,28 @@
<template lang="pug">
van-nav-bar(:title="title",
:fixed="true"
:left-arrow="showLeft",
@click-left='onClickLeft')
</template>
<script lang="ts">
import {Component, Provide, Vue, Prop} from 'vue-property-decorator';
@Component
export default class Navbar extends Vue {
// 标题栏文字
@Prop({ default: '' }) readonly title: String | undefined
// 是否展示左边返回按钮
@Prop({ default: false }) readonly showLeft: boolean | undefined
$router: any;
private onClickLeft(): void {
this.$router.go(-1)
}
}
</script>
<style scoped lang="stylus">
.van-nav-bar .van-icon
color #666
</style>

View File

@@ -0,0 +1,92 @@
<template lang="pug">
.NewsItem
.news_item(v-for="(item,index) in itemArr" :key="index")
.item_title
h2 {{item.status}}
span {{item.time}}
.item_content
.content_left
.name
img(:src="item.img")
span {{item.shop_name}}
p.yj 佣金:¥{{item.money}}
p.df 垫付:¥{{item.dfmoney}}
p.yj 审核倒计时:{{item.dtime}}
.content_right(@click="showInfo(item.id)") 查看任务
</template>
<script lang="ts">
import {Component, Prop, Vue} from 'vue-property-decorator';
@Component
export default class NewsItem extends Vue {
@Prop({default: [] }) readonly itemArr!: Array<object>;
async created() {
}
public showInfo(id: number): void{
alert(`查看任务,任务id为:${id}`)
}
}
</script>
<style scoped lang="stylus">
.news_item
padding 0.44rem 0.493rem
margin-bottom 0.253rem
border-bottom 10px solid #f6f6f6
.item_title
padding-left 0.28rem
padding-bottom 0.2rem
padding-right 0.28rem
box-sizing border-box
display flex
align-items center
justify-content space-between
border-bottom 1px solid #e5e5e5
h2
color #666
font-size 16px
span
color #adadad
font-size 13px
.item_content
padding-left 0.573rem
padding-right 0.4rem
padding-top 0.187rem
display flex
justify-content space-between
box-sizing border-box
.content_left
.name
padding-bottom 0.293rem
img
border-radius 100%
vertical-align middle
span
color #333
font-size 12px
padding-left 0.147rem
vertical-align middle
.yj
color #ff5858
font-size 12px
padding-bottom 0.293rem
.df
color #999
font-size 12px
padding-bottom 0.293rem
.content_right
color #20c6c2
font-size 14px
padding 0.147rem 0.28rem
background #e5fffe
border-radius 6px
line-height 0.667rem
box-sizing border-box
align-self center
</style>

View File

@@ -0,0 +1,62 @@
<template lang="pug">
.popup
van-popup(v-model="showPopup")
.popup_center
.title {{title}}
.text {{text}}
.popup_button(@click='isShow') 确定
</template>
<script lang="ts">
import {Component, Prop, Vue, Watch} from 'vue-property-decorator';
@Component
export default class Popup extends Vue {
// 控制开启关闭
@Prop({default: false}) readonly show!: boolean
// 标题
@Prop({default: '标题'}) readonly title!: string
// 内容描述
@Prop({default: '内容'}) readonly text!: string
// 点击确定关闭跳转的url不穿只关闭
@Prop({default: ''}) readonly url!: string
private showPopup: boolean = false
private isShow(): void {
this.showPopup = false
this.$emit('close')
if(this.url !== ''){
this.$router.push(this.url)
}
}
@Watch('show')
private watchShow(n:boolean, o:boolean) {
this.showPopup = true
}
}
</script>
<style lang="stylus" scoped>
.popup_center
background-color #ffffff
width 8.13rem
padding 1.09rem 0 0.72rem 0
text-align center
.title
font-size 0.45rem
font-weight 500
.text
font-size 0.4rem
color #999999
margin-top 0.4rem
.popup_button
width 4.43rem
height 1rem
text-align center
line-height 1rem
margin 0.65rem auto 0
background-color rgba(172,32,33,1)
color #ffffff
font-size 0.4rem
border-radius 1rem
</style>

View File

@@ -0,0 +1,37 @@
<template lang="pug">
van-tabbar(class="tabbar" v-model='active' route active-color='#cb4a45')
van-tabbar-item(:icon='item.icon' :to="item.href" v-for="(item,index) in TabBarList" :key="index") {{item.title}}
</template>
<script lang="ts">
import {Component, Provide, Vue, Watch} from 'vue-property-decorator';
import { Getter } from 'vuex-class'
@Component
export default class Tabbar extends Vue {
@Getter('getActivityRecord') getActivityRecord : any
private active: number = 0;
private TabBarList: Array<Object> = [];
created(){
setTimeout(() => {
this.watchGetActivityRecord()
}, 1000);
}
@Watch('imgData')
private watchGetActivityRecord(): void {
this.TabBarList = [
{ title: '首页', icon: this.getActivityRecord.first_image, href: '/' },
{ title: '排行榜', icon: this.getActivityRecord.charts_image, href: '/ranking' },
{ title: '活动说明', icon: this.getActivityRecord.des_image, href: '/description' },
];
}
}
</script>
<style scoped lang="stylus">
.tabbar
background #f6f6f6
</style>

View File

@@ -0,0 +1,81 @@
<template lang="pug">
.TaskHallItem
.list-item(v-for="(item,index) in item" :key="index")
.status(:class="item.active ? 'active-on' : 'active-close' ")
.item-left
img(:src="item.url")
.left-info
p {{ item.title }}
span {{ item.info }}
.item-right 已经认证
</template>
<script lang="ts">
import {Component, Provide, Vue, Prop} from 'vue-property-decorator';
@Component
export default class TaskHallItem extends Vue {
private titleName: String = "首页";
@Prop({default: [] }) readonly item!: Array<any>
async created() {
}
}
</script>
<style scoped lang="stylus">
.active-on
background #20c6c2
.active-close
background #e5e5e5
.TaskHallItem
margin-top: 0.48rem;
.list-item
display flex
justify-content space-between
padding 0.28rem 0.307rem
box-sizing border-box
align-items center
width 95%
margin 0 auto
box-shadow 0 2px 10px #0a10143d
margin-bottom 0.293rem
position relative
.status
width 0.347rem
height 0.347rem
border-radius 100%
position absolute
left 0.16rem
top 0.133rem
.item-left
img
width 1.507rem
height 1.507rem
margin-right 0.587rem
float left
border-radius 100%
.left-info
float left
padding-top 5px
p
color #333333
font-size 16px
span
color #999999
font-size 14px
.item-right
width 1.707rem
height 0.613rem
background #1ab51f
text-align center
color #fff
font-size 12px
line-height 0.643rem
</style>

View File

@@ -0,0 +1,17 @@
<template lang="pug">
.test
h3 测试子组件
</template>
<script lang="ts">
import {Component, Provide, Vue} from 'vue-property-decorator';
@Component
export default class Test extends Vue {
}
</script>
<style scoped lang="stylus">
</style>

View File

@@ -0,0 +1,303 @@
<template>
<div class="myWaterfall" v-show="imgsArr" ref="fallbox">
<ul>
<!--鍒楃殑瀹藉害搴旂敱璁$畻鐨勬潵-->
<li v-for="(it,index) in col" :style="{width:(100/col-1)+'%'}" :ref="'col'+index" :key="it">
<div v-for="goods in renderList[index]" :key="goods.ID">
<slot :goods="goods" />
</div>
</li>
</ul>
<div class="loading" v-show="rendering">
<div class="wait">
<span>L</span>
<span>o</span>
<span>a</span>
<span>d</span>
<span>i</span>
<span>n</span>
<span>g</span>
...
</div>
</div>
<p class="nomore" v-if="over">娌℃湁鏇村浜<EFBFBD>>>></p>
</div>
</template>
<script>
export default {
props: ["col", "onReachBottom"],
data() {
return {
loading: false, //鏄惁澶勪簬鍔犺浇鐘舵€侊紝鐢ㄤ簬瑙﹀簳浜嬩欢鐨勮妭娴<E5A6AD>
nowPage: 1,
imgsArr: [], //鏁版嵁鍒楄〃
over: false, //鏄惁宸茬粡鍒板簳浜<E7B0B3>
lock: false, //璇锋眰閿<E79CB0>
rendering: false, //娓叉煋涓<E7858B>,闃叉娓叉煋鏈畬鎴愰珮搴﹁幏鍙栦笉鍑嗙‘鑰屽鑷村彲杩炵画瑙﹀彂璇锋眰
renderList: [] //娓叉煋鍒楄〃锛屾牴鎹甶mgsArr+col鍒濆鍖栦负浜岀淮鏁扮粍
};
},
methods: {
computedOffset(obj, prop) {
//宸ュ叿鍑芥暟,璁$畻鍏冪礌鍒癰ody鐨勭粷瀵逛綅缃<E7B685>
if (obj == document.body || obj.offsetParent == document.body) {
return parseInt(obj[prop]);
}
return parseInt(obj[prop]) + this.computedOffset(obj.offsetParent, prop);
},
getDataList() {
//鏁版嵁鍔犺浇
//鑺傛祦澶勭悊
if (!this.loading && !this.over) {
//涓嶅浜庡姞杞界姸鎬佷笖鏈夋柊鏁版嵁
let self = this;
this.loading = true;
//椤垫暟澧炲姞
this.onReachBottom(self.nowPage).then(res => {
//鎷兼帴
self.rendering = true; //娓叉煋鐘舵€佷腑
if (res.data.list.length > 0) {
self.nowPage++;
let len = self.imgsArr.length;
self.imgsArr = self.imgsArr.concat(res.data.list);
self.fullData(len); //浠呭鏂扮殑鏁版嵁鍋氭覆鏌擄紝闇€瑕佷粠鍘熸暟缁勭殑缁堢偣寮€濮<E282AC>
self.lock = false;
} else {
//娌℃湁鏂版暟鎹<E69A9F>
self.over = true;
self.rendering = false;
}
self.loading = false;
});
} else {
//澶勪簬璇锋眰鐘舵€侊紝鑺傛祦,鎴栧凡鍑虹幇鏃犳暟鎹紙over锛夛紝蹇界暐璇锋眰
return false;
}
},
//姣旇緝鍒楃殑楂樺害鏉ュ垽鏂悜鍝竴涓垪涓坊鍔犳暟鎹<E69A9F>
fullData(index) {
if (index < this.imgsArr.length) {
let self = this;
let newImg = new Image();
/****************姝ゅ闇€浠ヤ釜浜虹殑鏁版嵁鏍煎紡璋冩暣*******************/
newImg.src = self.imgsArr[index].img;
newImg.onload = () => {
//闇€绛夊緟鍥剧墖鍔犺浇锛屽惁鍒欓珮搴︿笉鍑嗙‘
let colHeightList = []; //鎵€鏈夊垪鐨勯珮搴﹁〃
for (let i = 0; i < self.col; i++) {
colHeightList[i] = self.$refs["col" + i][0].offsetHeight;
}
//鑾峰彇鏈€灏忓垪
var min = colHeightList.indexOf(Math.min.apply(Math, colHeightList));
// self.renderList[min].push(self.imgsArr[index]);
let tar = self.renderList[min].concat(self.imgsArr[index]);
//闇€瑕佹洿鏂拌鍥撅紝涓婇潰鐨勪娇鐢╬ush涓嶄細鏇存柊瑙嗗浘锛堟搷浣滅殑绗簩缁达級,浣跨敤set
self.$set(self.renderList, min, tar);
self.fullData(index + 1);
};
} else {
this.rendering = false;
}
}
},
mounted() {
//娓叉煋鍒<E7858B> 鍒楄〃,鏍规嵁濡傜殑col鐢熸垚瀵瑰簲鍒楁暟,骞剁疆涓虹┖鐨勪簩缁存暟缁<E69A9F>
for (var i = 0; i < this.col; i++) {
this.renderList[i] = [];
}
//璇锋眰棣栨鏁版嵁:
this.getDataList();
let self = this;
window.onscroll = function(e) {
//鐩戞祴瑙﹀簳
//鐎戝竷娴侀珮搴<E78FAE> + 鐎戝竷娴佺殑鐩稿top < 鍙鍖洪珮搴<E78FAE>+婊氬姩璺濈 ==瑙﹀簳
//鑾峰彇鍒扮€戝竷娴佺洅瀛<E6B485>
let box = self.$refs.fallbox;
//鑾峰彇鍒扮洅瀛愮浉瀵逛簬鏂囨。鐨勪綅缃<E7B685>
let top = self.computedOffset(box, "offsetTop");
let height = box.offsetHeight;
//鍙鍖洪珮搴<E78FAE>
let clientHeight = document.documentElement.clientHeight;
//婊氬姩璺濈
let scrollTop = document.documentElement.scrollTop;
if (
top + height < clientHeight + scrollTop + 50 &&
!self.lock &&
!self.rendering
) {
//瑙﹀簳鍒ゆ柇,50鐢ㄤ簬鎻愬墠瑙﹀彂,涓嶇敤瀹屽叏鍒板簳鎵嶈Е鍙<D095>
//瑙﹀簳鎴愬姛
self.lock = true;
self.getDataList();
}
};
this.fullData(0);
},
beforeDestroy() {
//鍙栨秷婊氬姩浜嬩欢锛岄噸瑕侊紝鍚﹀垯璺敱璺宠浆鍚庢墽琛宻croll浜嬩欢灏嗕細鏈変竴鍫嗙殑undefined
window.onscroll = null;
//婊氬姩鏉$疆椤讹紝鍚﹀垯璺敱璺宠浆鍚庢粴鍔ㄦ潯鐨勪綅缃病鏈夊彉鍖<E5BD89>
document.documentElement.scrollTop = 0;
}
};
</script>
<style lang="stylus" >
@keyframes jump {
0% {
top: -10px;
}
100% {
top: 10px;
}
}
.loading {
position: fixed;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.2);
top: 0;
left: 0;
.wait {
font-size: 14px;
background: rgba(0, 0, 0, 0.8);
border-radius: 10px;
line-height: 50px;
font-weight: 900;
width: 200px;
height: 50px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
letter-spacing: 2px;
span {
font-size: 20px;
position: relative;
}
span:first-of-type {
color: red;
animation: jump 0.8s linear alternate-reverse infinite;
}
span:nth-of-type(2) {
color: orange;
animation: jump 0.8s linear 0.3s alternate-reverse infinite;
}
span:nth-of-type(3) {
color: yellow;
animation: jump 0.8s linear 0.6s alternate-reverse infinite;
}
span:nth-of-type(4) {
color: green;
animation: jump 0.8s linear 0.9s alternate-reverse infinite;
}
span:nth-of-type(5) {
color: cyan;
animation: jump 0.8s linear 1.2s alternate-reverse infinite;
}
span:nth-of-type(6) {
color: blue;
animation: jump 0.8s linear 1.5s alternate-reverse infinite;
}
span:nth-of-type(7) {
color: purple;
animation: jump 0.8s linear 1.8s alternate-reverse infinite;
}
}
}
.myWaterfall {
width: 100%;
height: 100%;
.nomore {
color: grey;
height: 30px;
line-height: 30px;
}
ul {
/*display: flex;*/
overflow: hidden;
padding: 10px;
background: whitesmoke;
border-radius: 10px;
li {
/*overflow: hidden;*/
/*flex: 1;*/
float: left;
/*width:25%;*/
margin: 0 5px;
color: #444;
overflow: hidden;
.goodsA:hover {
color: darkorange;
background-color: rgba(223, 234, 200, 0.1);
}
.goodsA {
width: 100%;
cursor: pointer;
box-sizing: border-box;
border: 1px solid #ddd;
box-shadow: 3px 1px 3px 0 grey;
background: red;
margin-bottom: 20px;
background-color: #fff;
img {
width: 100%;
}
.goodsInfo {
width: 100%;
.goodsName {
font-weight: 900;
font-size: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
height: 40px;
line-height: 40px;
text-indent: 10px;
}
.description {
font-size: 12px;
text-align: left;
text-indent: 10px;
/*height:25px;*/
line-height: 25px;
}
.price {
height: 30px;
line-height: 30px;
font-size: 10px;
text-align: left;
text-indent: 10px;
position: relative;
.nowPrice {
color: rgb(253, 132, 18);
font-weight: 900;
font-size: 18px;
margin-right: 10px;
em {
font-size: 24px;
}
}
.originPrice {
font-size: 14px;
text-decoration: line-through;
color: #999;
}
.sale {
position: absolute;
right: 5px;
top: 5px;
color: #444;
}
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1 @@
b55bf0ce-3b2f-4c02-83ed-7e5c6e108980

View File

@@ -0,0 +1,66 @@
<template lang="pug">
.description.content
.pd32.mt32
.description_center
div(v-html='getActivityRecord.content')
.tiem_button.flex_cen
div(@click='participate')
img(src='image/time_button.png')
| 报名参加
</template>
<script lang="ts">
import {Component, Provide, Vue, Inject} from 'vue-property-decorator';
import { Getter } from 'vuex-class'
@Component({
components: {}
})
export default class Description extends Vue {
$http: any
@Inject() readonly ShowTabBar!: Function
@Getter('getActivityRecord') getActivityRecord: any
created() {
this.ShowTabBar()
document.body.style.backgroundColor = '#cb4a45'
}
beforeRouteLeave (to: any, from: any, next: any) {
this.ShowTabBar()
next()
}
// 前往报名
private async participate(): Promise<void> {
let res = await this.$http.PublicService.isUp()
if(res !== 0){
this.$router.push('/participate')
}
// this.$router.push('/participate')
}
}
</script>
<style lang="stylus" scoped>
.description_center
background-color #ffffff
box-sizing border-box
padding 0.51rem
.tiem_button
font-size 0.43rem
background-color #ad2022
width 4rem
height 1.07rem
line-height 1.07rem
margin 0.4rem auto 0
border-radius 0.1rem
color #ffffff
img
height 0.45rem
width 0.45rem
margin-right 0.1rem
a
color #ffffff
table
width 100% !important
</style>

View File

@@ -0,0 +1,422 @@
<template lang="pug">
.index.content
van-swipe(class="my-swipe" :autoplay="3000" indicator-color="white")
van-swipe-item(v-for='(item, index) in bannerList' :key='index')
img.banner( :src="item.image" )
.pd32
.cumulative.flex.mt32
.flex1.flex_cen
.cumulative_item
p.item_p1 {{indexData.works}}
p.item_p2 报名人数
.flex1.flex_cen
.cumulative_item
p.item_p1 {{indexData.poll}}
p.item_p2 累积投票
.pd32
.time.mt32.flex_cen
div
.time_text 距离活动结束还有
.time_html(v-html='countDown')
.tiem_button.flex_cen
div(@click='participate')
img(src='image/time_button.png')
| 报名参加
.pd32.mt32
.search.flex
input.flex1(placeholder='搜索编号或名字' v-model='search')
img(src='image/sousuo.png' @click='searchClick')
.mt32
van-tabs(type="card" color='#ad2022' title-inactive-color='#000000' @change='tabsChange')
van-tab(title="全部")
van-tab(title="最新")
van-tab(title="排行")
.pd32.mt32
//- ImgList( :imgData='imgData' @vote='vote' @load="onLoad")
//- Cascade(:imgData='imgData' @load="onLoad" ref='emptys')
.image
.waterfall
.column(v-for="(item,list) in imageList" :key="list")
.column-item.cell_row(v-for="(obj,index) in item" :key="index" @click='goUrl(obj.id)')
div(style='background-color:#ffffff')
.row_img
img( :src='obj.image')
.number 编号: {{obj.num}}
.row_text
.title.flex
.name.flex1 {{obj.user_name}}
.num.flex1 {{obj.poll}}票
.row_button.flex_cen
img(src='image/time_button.png')
| 投票
popup(:show='show' title='投票成功' text='感谢您的参与' @close='close')
</template>
<script lang="ts">
import {Component, Provide, Vue, Inject} from 'vue-property-decorator';
import ImgList from "@/components/ImgList.vue";
import Cascade from "@/components/Cascade.vue";
import popup from '@/components/Popup.vue'
import { Getter } from 'vuex-class'
@Component({
components: {
ImgList,
popup,
Cascade
}
})
export default class Index extends Vue {
$http: any
$utils: any
$refs: any
@Getter('getActivityRecord') getActivityRecord: any
@Inject() readonly ShowTabBar!: Function
private bannerList: any[] = []
private indexData: any = {}
private imgData: any[] = []
// 作品列表
private finished: boolean = false
private loading: boolean = false
// 当前选中的tabs 下标+1
private type: number = 1
// 分页
private page: number = 1
private limit: number = 10
// 搜索
private search: string = ''
// 倒计时
private countDown: string = ''
// 控制弹出层
private show: boolean = false
//
private tempImage: any[] = []
private imageHeight: any[] = [0, 0]
private imageWidth: number = 0
private imageList: any[] = [[],[]]
private footerVisible: boolean = false
private height1: any
private height2: any
private height3: any
private available: any
private pagination: any = {
currentPage: 1,
totalPage: 10,
pageSize: 10,
available: true
}
async created() {
this.ShowTabBar()
this.getBanner()
this.getIndex()
setInterval(()=>{
this.leftTimer()
},1000)
document.body.style.backgroundColor = '#cb4a45'
if(!this.$utils.getToken() && this.$route.query.code == void 0){
this.login()
}
if(this.$route.query.code !== void 0 && !this.$utils.getToken()){
this.getToken(this.$route.query.code as string)
}
}
mounted() {
let doc: any = document.querySelector(".waterfall>.column")
this.imageWidth = doc.offsetWidth
window.addEventListener("scroll", this.pullDown);
}
destroyed() {
window.removeEventListener("scroll",this.pullDown)
}
beforeRouteLeave (to: any, from: any, next: any) {
this.ShowTabBar()
next()
}
private async getBanner(): Promise<void> {
let res = await this.$http.PublicService.getBanner()
this.bannerList = res
}
// 前往报名
private async participate(): Promise<void> {
let res = await this.$http.PublicService.isUp()
if(res !== 0){
this.$router.push('/participate')
}
}
// tab切换
private tabsChange(e: any): void {
this.imgData = []
this.search = ''
this.type = e+1
this.page = 1
this.finished = false
this.imageHeight = [0,0]
this.imageList = [[],[]]
this.footerVisible = false
this.loading = false
this.getIndex()
}
// 触底
private async onLoad(): Promise<void> {
if(!this.finished){
this.page++
await this.getIndex()
}else{
this.$toast('已经到底了')
}
}
// 前往详情
private goUrl(id: string): void{
this.$router.push(`/info?id=${id}`)
}
// 搜索
private searchClick(): void{
this.imgData = []
this.page = 1
this.finished = false
this.imageHeight = [0,0]
this.imageList = [[],[]]
this.footerVisible = false
this.loading = false
this.getIndex()
}
// 前往登录
private login(): void {
let url = encodeURIComponent(window.location.href); // 注意一定要encodeURIComponent
window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxe3a7b46f9329f666&redirect_uri=${url}&response_type=code&scope=snsapi_userinfo&state=#wechat_redirect`
}
// 登录获取token
private async getToken(code: string): Promise<void> {
let res = await this.$http.PublicService.login(code)
this.$utils.setStorage('access_token', res.token)
}
// 首页信息
private async getIndex(): Promise<void> {
let data = {
type: this.type,
name: this.search,
page: this.page,
limit: this.limit
}
let res = await this.$http.PublicService.getIndex(data)
this.indexData = res
this.imgData = [...res.data]
this.pushImage(0)
if(res.data.length < 10){
this.finished = true
}
}
private pushImage(index: number) {
if (index >= this.imgData.length){
return
}else{
this.loading = true
}
let img = new Image(),that = this;
img.src = this.imgData[index].image;
img.onload = () => {
let min = that.imageHeight[0],imageIndex = 0;
that.imageHeight.forEach(function(item, _index) {
if (min > item) {
min = item;
imageIndex = _index;
}
});
that.imageHeight[imageIndex] += (img.naturalHeight / img.naturalWidth + 96 / this.imageWidth);
that.imageList[imageIndex].push(that.imgData[index]);
that.pushImage(index + 1);
};
}
private pullDown(): void {
if(!this.loading) return;
// 获取滚轮位置
this.height1 =
window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop;
// 文档高度
this.height2 = document.body.scrollHeight;
// 可视区域
// 设置compatMode兼容IE
this.height3 =
document.compatMode === "CSS1Compat"
? document.documentElement.clientHeight
: document.body.clientHeight;
// 如果滚动到最低(这里设置离最底还有100距离才触发函数)
// available条件是为了防止触底时一直不断地请求。因此,请求一次后available设为0,直到滚动到离底部超过100距离(即数据加载玩后)才设为1
if (this.height3 + this.height1 >= this.height2 - 100 && this.available) {
this.available = false;
//请求下一页,如果当前页等于最大页,直接返回
if (this.pagination.currentPage >= this.pagination.totalPage) {
this.footerVisible = true;
return;
}
this.pagination.currentPage++;
// this.getImgData();
this.onLoad()
} else if (this.height3 + this.height1 < this.height2 - 100) {
this.available = true;
}
}
// 投票
private async vote(id: string): Promise<void> {
let res = await this.$http.PublicService.vote(id)
if(res !== 0){
this.show = !this.show
}
}
// 点击弹出层的确定时
private close() {
this.imgData = []
this.page = 1
this.finished = false
this.getIndex()
}
// 计算倒计时
private leftTimer(){
//3.获取现在的时间
var presentDate = new Date();
//4.获取时间戳
var funtureTime = this.getActivityRecord.end_time * 1000;
var presenTime = presentDate.getTime();
//5.获取剩余的时间戳
var allTime = funtureTime - presenTime;
//6.把毫秒转换为秒
var allSecond: any = (allTime / 1000).toFixed(0)
//7.获取剩余多少天
var day = this.size((allSecond / 3600 / 24).toFixed(0))
//8.获取剩余多少小时
var hour = this.size((allSecond / 3600 % 24).toFixed(0))
//9.获取剩余多少分钟
var minute = this.size((allSecond / 60 % 60).toFixed(0));
//10.获取剩余多少秒
var second = this.size((allSecond % 60).toFixed(0));
this.countDown = `<span style='color:#ac2021;font-size:0.52rem;'>${day}</span>天
<span style='color:#ac2021;font-size:0.52rem;'>${hour}</span>时 <span style='color:#ac2021;font-size:0.52rem;'>${minute}</span>分 <span style='color:#ac2021;font-size:0.52rem; '>${second}</span>秒`
}
private size(num: any) {
return num < 10 && num >= 0 ? '0' + num : num;
}
}
</script>
<style lang="stylus" scoped>
@import "~@assets/stylus/page/index.styl"
// html
// background-color #cb4a45
</style>
<style lang="stylus">
.index
.van-tabs__wrap
height 1.07rem
.van-tabs__nav--card
margin 0
height 1.07rem
.van-tab
line-height 1.07rem
span
font-size:0.43rem
</style>
<style lang="stylus" scoped>
.waterfall
margin-top 10px
display flex
.column
width 50%
.column-item
width 100%
padding 0.1rem
box-sizing border-box
img
width 100%
height 100%
.date
margin-bottom 10px
.title
margin-bottom 10px
.content
margin-bottom 10px
.cell_row
margin-top 0.15rem
border-radius 0.2rem
width 98%
display inline-block
box-sizing border-box
.row_img
position relative
img
width 100%
height auto
.number
position absolute
right 0
top 0.24rem
width 1.67rem
height 0.67rem
background-color rgba(0,0,0,.6)
text-align center
line-height 0.67rem
border-radius 0.1rem 0 0 0.1rem
color #ffffff
font-size 0.35rem
.row_text
padding 0.32rem 0.21rem
.title
.name
font-size 0.4rem
.num
font-size 0.4rem
text-align right
color rgba(172,32,33,1)
.row_button
font-size 0.43rem
background-color #ad2022
width 100%
height 1.07rem
line-height 1.07rem
color #ffffff
margin 0.2rem auto 0
border-radius 2rem
img
height 0.45rem
width 0.45rem
margin-right 0.1rem
.footer
width 100%
height 80px
display flex
justify-content center
align-items center
border-top 10px
color #ffffff
margin-top 10px
</style>

View File

@@ -0,0 +1,141 @@
<template lang="pug">
.info
.info_img
img(:src='info.avatar')
.info_name {{info.num}}号-{{info.user_name}}
.pd32
.cumulative.flex.mt32
.flex1.flex_cen
.cumulative_item
p.item_p1 {{info.rank}}
p.item_p2 排名
.flex1.flex_cen
.cumulative_item
p.item_p1 {{info.poll}}
p.item_p2 票数
.pd32.mt32
.works
.works_title 作品主题
.works_text {{info.title}}
.works_title.mt40 参赛照片
.works_img
img(v-for='item in info.imgs' :src='item.image')
.poster_button(@click='poster') 生成海报分享
.tabbar.flex
router-link.flex1.flex_cen(to='/')
img.tabbar_img(src='image/home.png')
| 首页
.flex1.piao
.vote.flex_cen(@click='vote') 投票
router-link.flex1.flex_cen(to='/ranking')
img.tabbar_img(src='image/paihangbang.png')
| 排行榜
popup(:show='show' title='投票成功' text='感谢您的参与' @close='close')
.poster(@click='poster') 分享
</template>
<script lang="ts">
import {Component, Provide, Vue, Inject} from 'vue-property-decorator';
import popup from '@/components/Popup.vue'
@Component({
components: {
popup
}
})
export default class Info extends Vue {
$http: any
$utils: any
@Inject() HiddenTabBar!: Function
// 作品id
private id: string = ''
// 作品数据
private info: any = {}
// 控制弹出层
private show: boolean = false
async created() {
document.body.style.backgroundColor = '#cb4a45'
this.HiddenTabBar()
if(this.$route.query.id !== void 0){
this.id = this.$route.query.id as string
this.worksDetail()
this.wxConfig()
}
if(!this.$utils.getToken() && this.$route.query.code == void 0){
this.login()
}
if(this.$route.query.code !== void 0 && !this.$utils.getToken()){
this.getToken(this.$route.query.code as string)
}
}
mounted() {
}
// 前往登录
private login(): void {
let url = encodeURIComponent(window.location.href); // 注意一定要encodeURIComponent
window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxe3a7b46f9329f666&redirect_uri=${url}&response_type=code&scope=snsapi_userinfo&state=#wechat_redirect`
}
// 登录获取token
private async getToken(code: string): Promise<void> {
let res = await this.$http.PublicService.login(code)
this.$utils.setStorage('access_token', res.token)
}
// 获取用户作品信息
private async worksDetail(): Promise<void> {
let info = await this.$http.PublicService.worksDetail(this.id)
this.info = info
document.title = `${this.info.num}号-${this.info.user_name}-【马上洗】有奖征集微影像`
}
// 投票
private async vote(): Promise<void> {
if(!this.$utils.getToken() && this.$route.query.code == void 0){
this.login()
return
}
let res = await this.$http.PublicService.vote(this.id)
if(res !== 0){
this.show = !this.show
}
}
// 点击弹出层的确定时
private close() {
this.worksDetail()
}
// 去往生成海报页面
private poster() {
this.$router.push({path:'/poster',query:{url:window.location.href, id: this.id }})
}
private async wxConfig(): Promise<void> {
let data = await this.$http.PublicService.getSignPackage()
this.$utils.wxConfig(data)
this.$utils.wxSdk((res:any)=>{
res.updateAppMessageShareData({
title: `${this.info.num}号-${this.info.user_name}-【马上洗】有奖征集微影像`,
desc: `${this.info.num}号-${this.info.user_name}-【马上洗】有奖征集微影像`
})
res.updateTimelineShareData({
title: `${this.info.num}号-${this.info.user_name}-【马上洗】有奖征集微影像`
})
res.onMenuShareAppMessage({
title: `${this.info.num}号-${this.info.user_name}-【马上洗】有奖征集微影像`,
desc: `${this.info.num}号-${this.info.user_name}-【马上洗】有奖征集微影像`
})
})
}
}
</script>
<style lang="stylus" scoped>
@import "~@assets/stylus/page/info.styl"
</style>

View File

@@ -0,0 +1,141 @@
<template lang="pug">
.participate.content
.toutu
img.img(:src='getActivityRecord.enroll_image')
MyPanel(title='参加活动')
van-field( v-model="name" label="姓名" placeholder='输入姓名')
van-field( v-model="tel" label="手机号" placeholder='输入手机号')
van-field( v-model="message" rows="2" autosize label="作品主题" type="textarea" maxlength="50" placeholder="请输入作品主题" show-word-limit)
.somCell
.title.flex
.left.flex1 上传参赛者照片
.right.flex1 可上传1-3张
.center
//- 图片上传
.updata_img
.preview_img(v-for='(item,index) in imgSrcUrl' :key='index')
img(:src='item')
van-icon.preview_close(name="close" @click='closeImg(index)')
.updata_click(@click='updataImg' v-if='imgSrcUrl.length < 3')
van-icon.preview_plus(name="plus")
.participate_button(@click='signUp') 提交
popup(:show='show' title='报名成功' text='感谢您的参与 我们会尽快完成审核' url='/')
</template>
<script lang="ts">
import {Component, Provide, Vue, Inject} from 'vue-property-decorator';
import { Getter } from 'vuex-class'
import MyPanel from '@/components/MyPanel.vue'
import popup from '@/components/Popup.vue'
@Component({
components: {
MyPanel,
popup
}
})
export default class Participate extends Vue {
$http: any
$utils: any
@Getter('getActivityRecord') getActivityRecord: any
private name: string = ''
private tel: string = ''
private message: string = ''
// 图片的base64编码用于预览
private imgSrcUrl: string[] = []
// 图片保存在服务器上的id
private imgSrcId: string[] = []
// 控制弹出层
private show: boolean = false
created(){
document.body.style.backgroundColor = '#ffffff'
this.wxConfig()
}
private async signUp(): Promise<void> {
if(this.name == ''){
this.$toast('请输入姓名')
return
}
if(this.tel == ''){
this.$toast('输入手机号')
return
}
if(this.message == ''){
this.$toast('请输入作品主题')
return
}
if(this.imgSrcUrl.length <= 0){
this.$toast('请上传参赛照片')
return
}
let data = {
user_name: this.name,
telnum: this.tel,
title: this.message,
img: [...this.imgSrcId]
}
let rel = await this.$http.PublicService.signUp(data)
if(rel !== 0){
this.show = !this.show
}
}
private async wxConfig(): Promise<void> {
let data = await this.$http.PublicService.getSignPackage()
this.$utils.wxConfig(data)
}
// 图片上传
private async updataImg(): Promise<void> {
let that = this
this.$utils.wxSdk((rel: any)=>{
rel.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function (r: any) {
rel.getLocalImgData({
localId: r.localIds[0], // 图片的localID
success: function (r1: any) {
const u = navigator.userAgent, app = navigator.appVersion;
const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1;
const s = 'data:image/jpg;base64,'
if(isAndroid){
that.imgSrcUrl.push(`${s}${r1.localData}`)
}else{
that.imgSrcUrl.push(r1.localData)
}
}
});
rel.uploadImage({
localId: r.localIds[0], // 需要上传的图片的本地ID,由chooseImage接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: function (r2: any) {
that.imgSrcId.push(r2.serverId)
},
fail: (err:any)=>{
console.log(err,"err");
}
});
}
});
})
}
// 删除图片
private closeImg(index: number) :void {
let imgSrcUrl = this.imgSrcUrl, imgSrcId = this.imgSrcId;
imgSrcUrl.splice(index, 1)
imgSrcId.splice(index, 1)
this.imgSrcUrl = imgSrcUrl
this.imgSrcId = imgSrcId
}
}
</script>
<style lang="stylus" scoped>
@import "~@assets/stylus/page/participate.styl"
</style>

View File

@@ -0,0 +1,51 @@
<template lang="pug">
.poster
.poster_img(v-if="imgUrl !== ''")
img.img( :src='imgUrl')
.poster_text 请长按保存图片
</template>
<script lang="ts">
import {Component, Provide, Vue, Inject} from 'vue-property-decorator';
@Component({
components: {}
})
export default class Poster extends Vue {
$http: any
@Inject() readonly HiddenTabBar!: Function
private imgUrl: string = ''
created() {
this.HiddenTabBar()
document.body.style.backgroundColor = '#f5f5f5'
if(this.$route.query.url !== void 0){
this.createPoster(this.$route.query.url as string, this.$route.query.id as string)
}
}
private async createPoster(url :string, id :string): Promise<void> {
let res = await this.$http.PublicService.createPoster(url,id)
this.imgUrl = res.poster
}
beforeRouteLeave (to: any, from: any, next: any) {
this.HiddenTabBar()
next()
}
}
</script>
<style lang="stylus" scoped>
.poster
padding 0.42rem 0
.poster_img
margin 0 0.32rem 0
background-color #ffffff
padding 0.32rem
.poster_text
text-align center
font-size 0.52rem
color #ad2022
</style>

View File

@@ -0,0 +1,74 @@
<template lang="pug">
.ranking.content
.toutu
img.img(src='image/paihang.png')
.pd32
.ranking_list
.title 票数排行榜
.ranking_row.flex(v-for='(item,index) in imgArr' :key='index')
.paihang_img(v-if='index == 0')
img(src='image/1.png')
.paihang_img(v-if='index == 1')
img(src='image/2.png')
.paihang_img(v-if='index == 2')
img(src='image/3.png')
.paihang(v-if='index > 2') {{index + 1}}
.ranking_img
img.img( :src='item.avatar')
.name.flex1 {{item.num}}号 {{item.user_name}}
.votes
span {{item.poll}}
| 票
.mt32.bottomLoad(@click='pageRank') 点击加载更多
</template>
<script lang="ts">
import {Component, Provide, Vue, Inject} from 'vue-property-decorator';
@Component({
components: {}
})
export default class Ranking extends Vue {
$http: any
@Inject() readonly ShowTabBar!: Function
private page: number = 1
private isPage: boolean = false
private imgArr: any[] = []
async created() {
this.ShowTabBar()
document.body.style.backgroundColor = '#cb4a45'
this.rank()
}
beforeRouteLeave (to: any, from: any, next: any) {
this.ShowTabBar()
next()
}
// 排行榜列表
private async rank(): Promise<void> {
let res = await this.$http.PublicService.rank(this.page)
if(!!res){
this.imgArr = [...this.imgArr,...res]
if(res.length <= 0){
this.isPage = true
}
}
}
private pageRank() :void {
if(!this.isPage){
this.page++
this.rank()
}else{
this.$toast('已经到底了')
}
}
}
</script>
<style lang="stylus" scoped>
@import "~@assets/stylus/page/ranking.styl"
</style>

View File

@@ -0,0 +1,17 @@
import Vue, {VNode} from 'vue';
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {
}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {
}
interface IntrinsicElements {
[elem: string]: any
}
}
}

View File

@@ -0,0 +1,5 @@
declare module '*.vue' {
import Vue from 'vue';
export default Vue;
}

View File

@@ -0,0 +1,40 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}

View File

@@ -0,0 +1,178 @@
const isProduction = process.env.NODE_ENV === 'production';
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const CompressionPlugin = require('compression-webpack-plugin')
const path = require('path');
module.exports = {
publicPath: './',
filenameHashing: true,
productionSourceMap: false,
parallel: require('os').cpus().length > 1,
lintOnSave: false,
devServer: {
hot: true,
hotOnly: true,
host: '0.0.0.0',
port: 9000,
compress: true,
open: true,
openPage: '#/',
overlay: {
warnings: true,
errors: true,
},
// proxy: {
// '/api': {
// target: 'https://test.yishaxiyi.com',
// changeOrigin: true,
// ws: true,
// pathRewrite: {
// '^/api': ''
// }
// }
// }
},
// pages: {
// index: {
// entry: 'src/core/run.ts',
// template: 'public/index.html',
// filename: 'index.html',
// title: 'H5 分享',
// chunks: ['chunk-vendors', 'chunk-common', 'index'],
// CurrentEnvironment: isProduction,
// cdn: {
// css: [
// 'https://cdn.bootcss.com/normalize/8.0.1/normalize.min.css',
// 'https://cdn.jsdelivr.net/npm/vant@2.2/lib/index.css',
// ],
// js: [
// 'https://cdn.bootcss.com/vue/2.6.10/vue.runtime.min.js',
// 'https://cdn.bootcss.com/vue-router/3.1.2/vue-router.min.js',
// 'https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js',
// 'https://cdn.jsdelivr.net/npm/vant@2.2/lib/vant.min.js'
// ],
// },
// },
// },
configureWebpack: (config) => {
// 设置程序核心入口
config.entry.app = './src/core/run/run.ts'
if (process.env.NODE_ENV === 'production') {
/**
* Gzip压缩
*/
config.mode = 'production'
return {
plugins: [new CompressionPlugin({
test: /\.js$|\.html$|\.css/, //匹配文件名
threshold: 10240, //对超过10k的数据进行压缩
deleteOriginalAssets: false //是否删除原文件
})]
}
/**
* 去除这些公共包,让webpack不打包到项目源码的js中,减小项目js体积
* 然后使用 pages 节点下的 cdn 数组里面的资源外链来引入。
*/
config.externals = {
vue: 'Vue',
'vue-router': 'VueRouter',
axios: 'axios',
Vant: 'Vant',
};
/**
* 部署阶段 代码压缩
*/
config.plugins.push(
// 生产环境自动删除console
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false,
drop_debugger: true,
drop_console: true,
},
},
sourceMap: false,
parallel: true,
}),
);
// 开启 BundleAnalyzerPlugin
return {
plugins: [
new BundleAnalyzerPlugin(),
],
};
}
},
chainWebpack: (config) => {
/**
* 如果为打包发布,那么修改 html-webpack-plugin 配置
* 不取消 骨架屏 的节点注释 vue-ssr-outlet
*/
if (process.env.NODE_ENV === 'production') {
config.plugin('html').tap(opts => {
opts[0].minify.removeComments = false
return opts
})
}
/**
* 移除不必要的 prefetch 和 preload请求
*/
config.plugins.delete('prefetch');
config.plugins.delete('preload');
/**
* html 模板引擎 pug 配置
*/
config.module.rule('pug')
.test(/\.pug$/)
.use('pug-html-loader')
.loader('pug-html-loader')
.end();
/**
* 导入全局css
* @type {*[]}
*/
const types = ['vue-modules', 'vue', 'normal-modules', 'normal'];
types.forEach(type => addStyleResource(config.module.rule('stylus').oneOf(type)));
/**
* 配置路径别名,项目中引用资源的时候路径可以更简短
* 但是ts文件在用的时候,编译器会报错,需要添加 @ts-ignore
* 忽略开发工具的错误。
*/
config.resolve.alias
.set('@', resolve('src/view'))
.set('@core', resolve('src/core'))
.set('@assets', resolve('src/view/assets'));
}
};
function resolve(dir) {
return path.join(__dirname, dir);
}
function addStyleResource(rule) {
rule.use('style-resource')
.loader('style-resources-loader')
.options({
patterns: [
path.resolve(__dirname, './src/view/assets/stylus/imports.styl')
],
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
1:https://www.showdoc.cc/yisha?page_id=3895088468428791 接口文档
2:https://lanhuapp.com/web/#/item/project/board?pid=12fcc1ca-dd4e-4db7-8b86-57bb7886425a 蓝湖