Commit 38185fe1 authored by niguangyjf's avatar niguangyjf

新建项目

parents
// Generated by 'unplugin-auto-import'
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const createPinia: typeof import('pinia')['createPinia']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const defineStore: typeof import('pinia')['defineStore']
const effectScope: typeof import('vue')['effectScope']
const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const mapActions: typeof import('pinia')['mapActions']
const mapGetters: typeof import('pinia')['mapGetters']
const mapState: typeof import('pinia')['mapState']
const mapStores: typeof import('pinia')['mapStores']
const mapWritableState: typeof import('pinia')['mapWritableState']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const storeToRefs: typeof import('pinia')['storeToRefs']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useSlots: typeof import('vue')['useSlots']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
{
"globals": {
"EffectScope": "readonly",
"acceptHMRUpdate": "readonly",
"computed": "readonly",
"createApp": "readonly",
"createPinia": "readonly",
"customRef": "readonly",
"defineAsyncComponent": "readonly",
"defineComponent": "readonly",
"defineStore": "readonly",
"effectScope": "readonly",
"getActivePinia": "readonly",
"getCurrentInstance": "readonly",
"getCurrentScope": "readonly",
"h": "readonly",
"inject": "readonly",
"isProxy": "readonly",
"isReactive": "readonly",
"isReadonly": "readonly",
"isRef": "readonly",
"mapActions": "readonly",
"mapGetters": "readonly",
"mapState": "readonly",
"mapStores": "readonly",
"mapWritableState": "readonly",
"markRaw": "readonly",
"nextTick": "readonly",
"onActivated": "readonly",
"onBeforeMount": "readonly",
"onBeforeUnmount": "readonly",
"onBeforeUpdate": "readonly",
"onDeactivated": "readonly",
"onErrorCaptured": "readonly",
"onMounted": "readonly",
"onRenderTracked": "readonly",
"onRenderTriggered": "readonly",
"onScopeDispose": "readonly",
"onServerPrefetch": "readonly",
"onUnmounted": "readonly",
"onUpdated": "readonly",
"provide": "readonly",
"reactive": "readonly",
"readonly": "readonly",
"ref": "readonly",
"resolveComponent": "readonly",
"setActivePinia": "readonly",
"setMapStoreSuffix": "readonly",
"shallowReactive": "readonly",
"shallowReadonly": "readonly",
"shallowRef": "readonly",
"storeToRefs": "readonly",
"toRaw": "readonly",
"toRef": "readonly",
"toRefs": "readonly",
"triggerRef": "readonly",
"unref": "readonly",
"useAttrs": "readonly",
"useCssModule": "readonly",
"useCssVars": "readonly",
"useSlots": "readonly",
"watch": "readonly",
"watchEffect": "readonly",
"watchPostEffect": "readonly",
"watchSyncEffect": "readonly"
}
}
\ No newline at end of file
# Auto detect text files and perform LF normalization
* text=auto
.DS_Store
node_modules
dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.project
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
.hbuilderx
.vscode/settings.json
MIT License
Copyright (c) 2022 VeigarChen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# uniapp-vue3-ts-fast
> 基于Vite+Vue3+uniapp的H5与微信小程序开发模版,超简洁!超便捷!单页面路由管理~
## 简介
> 全局只有一个路由,需要新增页面显示需要在src/router目录下routeList.ts和index.vue中配置,用于快速创建页面相对较少的微信小程序应用
## 使用技术
- Vue3
- Vite
- Typescript
- SCSS
- 封装 uni.request
- 集成 uni-ui
## 实现功能
- store
- router
- log日志打印
- 延迟定时器timer
- 顶部导航栏
- 底部导航栏
- fps显示
## usage
```bash
git clone https://github.com/a1518079148/uniapp-vue3-ts-fast.git
pnpm i
pnpm run dev:h5
pnpm run dev:mp-weixin
```
## Test
目前仅测试了微信小程序,其他平台需要自行测试
## 关于我
- QQ:1518079148
- 邮箱:1518079148@qq.com
\ No newline at end of file
<!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="/src/main.ts"></script>
</body>
</html>
This diff is collapsed.
{
"name": "uniapp-vue3-ts-fast",
"version": "1.0.0",
"scripts": {
"dev:app": "uni -p app",
"dev:custom": "uni -p",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni --mode development -p mp-weixin",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build:app": "uni build -p app",
"build:custom": "uni build -p",
"build:h5": "uni build",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-app-plus": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-components": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-h5": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-mp-alipay": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-mp-baidu": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-mp-kuaishou": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-mp-lark": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-mp-qq": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-mp-toutiao": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-mp-weixin": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-quickapp-webview": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-ui": "^1.4.20",
"@rollup/plugin-alias": "^4.0.2",
"@rollup/plugin-commonjs": "^23.0.2",
"axios": "^1.1.3",
"element-plus": "^2.2.19",
"lodash": "^4.17.21",
"vue": "^3.2.4"
},
"devDependencies": {
"@dcloudio/types": "^3.0.7",
"@dcloudio/uni-automator": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-cli-shared": "3.0.0-alpha-3050320220727002",
"@dcloudio/uni-stacktracey": "3.0.0-alpha-3050320220727002",
"@dcloudio/vite-plugin-uni": "3.0.0-alpha-3050320220727002",
"@types/node": "^18.6.3",
"sass": "^1.54.0",
"typescript": "^4.7.4",
"unplugin-auto-import": "^0.10.3",
"vite": "2.9.14"
},
"engines": {
"node": ">=16.0.0"
}
}
<script setup lang="ts">
import { onHide, onLaunch, onLoad, onShow } from '@dcloudio/uni-app';
import clog from './utils/clog';
onLaunch(() => {
clog.info('App Launch')
})
onShow(() => {
clog.info('App Show')
})
onHide(() => {
clog.info('App Hide')
})
onLoad(() => {
clog.info('App Load')
})
</script>
<style lang="scss">
@import "@/uni_modules/uview-plus/index.scss";
</style>
/**
* account相关api
*/
import {
http
} from '@/api/service.js'
export const fetchCurrentUserInfo = () => {
return http.get(`/account-service/user/myInfo`)
}
\ No newline at end of file
/**
* 商品相关api
*/
import {
http
} from '@/api/service.js'
/**
* 查询商品列表
* @param {Object} params - 查询参数
*/
export const getGoodsList = (params) => {
return http.post('/authorization-server/oauth/token', {
params: {
...params,
scope: 'read',
grant_type: 'password'
},
})
}
// 通用请求方法middleware 演示。文档:https://www.quanzhan.co/luch-request/guide/3.x/#middleware
/**
* 查询商品信息
* @param {Object} data - 查询数据
* @param {Object} params - 查询params参数
*/
export const getGoodsInfo = (data, params) => {
return http.middleware({
method: 'POST', // 必须大写
url: '/api/user/update',
data: data,
params: params,
custom: {
auth: true
}
})
}
import * as user from './user'
export default {
user
}
/**
* 登录token相关api
*/
import {
http
} from '@/api/service.js'
interface tokenProps {
username: string;
password: string;
}
/**
* 获取token
* @param {Object} params - 查询参数
*/
export const fetchLoginToken = (params: tokenProps) => {
const url = `?username=${params.username}&password=${params.password}&scope=read&grant_type=password`
return http.post(`/authorization-server/oauth/token${url}`, {}, {
header: {
'Authorization': 'Basic YWRtaW5fY2xpZW50OmFkbWluX3NlY3JldA==',
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
}
})
}
/**
* @version 3.0.5
* @Author lu-ch
* @Email webwork.s@qq.com
* 文档: https://www.quanzhan.co/luch-request/
* github: https://github.com/lei-mu/luch-request
* DCloud: http://ext.dcloud.net.cn/plugin?id=392
* HBuilderX: beat-3.0.4 alpha-3.0.4
*/
import Request from '@/utils/luch-request/index.js'
import { omit } from 'lodash'
import router from '@/router';
const baseUrl = 'https://api.devops.thinker.vc'
const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
405: '请求方法不被允许。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。',
};
const getTokenStorage = () => {
let token = ''
try {
token = uni.getStorageSync('Authorization')
} catch (e) {}
return token
}
const handleError = (response) => {
if (response && response.statusCode) {
const errorText = response?.data?.message || codeMessage[response.statusCode];
const { statusCode } = response;
if (statusCode === 401) {
router.push('/prolist');
return false;
}
uni.showToast({
title: statusCode === 401 ? '登录过期,请重新登录!' : errorText,
icon:'none'
})
}
if (!response) {
uni.showToast({
title: '您的网络发生异常,无法连接服务器',
icon:'none'
});
}
}
const http = new Request()
http.setConfig((config) => {
/* 设置全局配置 */
config.baseURL = baseUrl /* 根域名不同 */
config.header = {
...config.header,
// a: 1, // 演示
// b: 2 // 演示
}
return config
})
http.interceptors.request.use((config) => {
/* 请求之前拦截器。可以使用async await 做异步操作 */
const token = getTokenStorage()
const inputToken = config.header.Authorization
config.header = {
...config.header,
Authorization: `bearer ${token}`
}
if (config?.url?.indexOf('/authorization-server/oauth/token') > -1) {
config.header.Authorization = inputToken;
}
console.log(config, `++++++++++++`)
if (!config.header.Authorization) { // 如果token不存在,return Promise.reject(config) 会取消本次请求
return Promise.reject(config)
}
return config
}, (config) => {
return Promise.reject(config)
})
http.interceptors.response.use(async (response) => {
console.log(response, `PPPPPPPPPPPPPPPPP`)
/* 请求之后拦截器。可以使用async await 做异步操作 */
if (response.statusCode !== 200 && response.code !== 200) { // 服务端返回的状态码不等于200,则reject()
return Promise.reject(response)
}
return response
}, async (response) => { // 请求错误做点什么。可以使用async await 做异步操作
console.log(response, `::::::::::::::::::::`)
await handleError(response)
return Promise.reject(response)
})
export {
http,
}
import http from "@/utils/http";
export function getUserId(id) {
return http.post('/api/v1/getUserinfo')
}
\ No newline at end of file
//h5 要调用的js文件
// #ifdef H5
// import amap from '@/utils/maps.js';
// #endif
//微信小程序要调用的js文件
// #ifdef MP
import amap from '@/utils/maps/amap-wx.js';
// #endif
//获取位置信息
const getlocation = (opt) => {
return new Promise((resolve, reject) => {
//h5开始
// #ifdef H5
AMap.plugin('AMap.Geolocation', function() {
uni.showLoading({
title: '系统正在定位'
});
var geolocation = new AMap.Geolocation({
enableHighAccuracy: true, //是否使用高精度定位,默认:true
timeout: 10000, //超过10秒后停止定位,默认:5s
buttonPosition: 'RB', //定位按钮的停靠位置
buttonOffset: new AMap.Pixel(10, 20), //定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
zoomToAccuracy: true, //定位成功后是否自动调整地图视野到定位点
});
geolocation.getCurrentPosition(function(status, result) {
if (status == 'complete') {
//这个地方的result,可能会出现报错:获得地理定位时间。得到ipLocation成功。获取地址失败,请检查您的密钥或网络。
//可能是密匙申请错了,重新申请密匙,生成maps.js文件。
console.log(result)
uni.hideLoading();
resolve(result)
} else {
uni.hideLoading();
uni.showToast({
title: '定位失败',
});
reject(result)
}
});
});
// #endif
//h5结束
//app开始
// #ifdef APP-PLUS
uni.showLoading({
title: '获取信息中'
});
uni.getLocation({
// map组件默认为国测局坐标gcj02,调用 uni.getLocation返回结果传递给组件时,需指定 type 为 gcj02
type: 'gcj02',
geocode: true,
success: function(data) {
resolve(data)
console.log(data)
},
fail: function(err) {
reject(err)
},
complete() {
uni.hideLoading();
}
})
// #endif
//app结束
///小程序开始
// #ifdef MP
var amapPlugin = new amap.AMapWX({
key: '8e7a51da1d2879*************' //此处为高德平台申请的微信小程序的key
});
uni.showLoading({
title: '获取信息中'
});
amapPlugin.getRegeo({
success: function(data) {
resolve(data)
},
fail: function(err) {
reject(err)
},
complete: function() {
uni.hideLoading();
}
});
// #endif
//小程序结束
})
};
export default {
getlocation: getlocation
}
\ No newline at end of file
<template>
<canvas id="globalCtx" type="2d" class="fit" />
<div v-if="show" style="position: fixed;top: 0px;left: 15px;z-index:200;color:#00000055;font-size:30rpx"
:style="{ marginTop: `${store.system.statusBarHeight + store.system.uniBarHeight}px` }">
FPS:{{ conf.maxfps }}
</div>
</template>
<script lang="ts" setup>
import store from '@/store';
import { getCurrentInstance, onMounted, reactive } from 'vue';
defineProps({
show: { default: true }
})
const conf = reactive({
fps: 0,
fpsList: [] as any[],
maxfps: undefined as any,
startTime: new Date().getTime()
})
onMounted(() => {
wx.createSelectorQuery()
.in(getCurrentInstance())
.select('#globalCtx')
.fields({ node: true, size: true })
.exec((res: any) => {
const canvas = res[0].node
const draw = () => {
if (conf.fps == 0) conf.startTime = new Date().getTime()
conf.fps++
if (new Date().getTime() - conf.startTime >= 1000) {
conf.startTime = new Date().getTime()
if (conf.fpsList.length > 5) {
let maxfps = conf.fpsList.reduce((sum, value) => { return sum + value }, 0) / conf.fpsList.length
conf.maxfps = maxfps.toFixed(0)
conf.fpsList.length = 0
}
conf.fpsList.push(conf.fps)
if (!conf.maxfps) conf.maxfps = conf.fps
conf.fps = 0
}
store.ctx.run()
canvas.requestAnimationFrame(draw)
}
canvas.requestAnimationFrame(draw)
})
})
</script>
<style lang="scss" scoped>
</style>
\ No newline at end of file
<template>
<div @click="switchTab(0)" :class="conf.class">
<div class="tab-bar-border"></div>
<view v-for="(item, index) in conf.list" :key="index" @click="switchTab(item)" class="tab-bar-item">
<img :src="router.item.item.pagePath === item.pagePath ? item.selectedIconPath : item.iconPath"
class="tab-bar-icon" />
<div :style="{ color: router.item.item.pagePath === item.pagePath ? conf.selectedColor : conf.color }"
class="tab-bar-text">{{
item.tabsText
}}</div>
</view>
</div>
</template>
<script setup lang="ts">
import router from '@/router';
import { reactive, watch } from 'vue';
const props = defineProps({
show: { default: false, }
})
const conf = reactive({
color: "#989898",
selectedColor: "#0052D9",
list: router.tabslist,
class: ['', 'tab-bar', 'tab-bar-show']
})
const switchTab = (item: any) => {
if (item.routerType === 'push')
router.push(item.pagePath)
else
router.clear(item.pagePath)
}
const show = () => {
if (props.show) conf.class[2] = 'tab-bar-show'
else conf.class[2] = 'tab-bar-hide'
}
watch(() => props.show, (val) => {
show()
})
show()
</script>
<style lang="scss">
.tab-bar {
z-index: 100;
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 70px;
background: white;
display: flex;
padding-bottom: env(safe-area-inset-bottom);
transition: all .3s;
}
.tab-bar-hide {
transform: translate(0px, 70px);
}
.tab-bar-border {
background-color: rgba(0, 0, 0, 0.33);
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 1px;
transform: scaleY(0.5);
}
.tab-bar-item {
flex: 1;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.tab-bar-item {
.tab-bar-icon {
width: 27px;
height: 27px;
}
.tab-bar-text {
font-size: 12px;
margin-top: 6px;
}
}
</style>
<template>
<div class="g-popup">
<uni-popup ref="popupRef" type="bottom" style="z-index:101">
<view class="show-modal">
<view class="title">{{ props.title }}</view>
<view v-if="content" class="modal-content">{{ props.content }}</view>
<view class="btn-box rowBC">
<view class="cc cancel" @click="onCancel">{{ cancelText }}</view>
<view class="cc sure" @click="onConfirm">{{ confirmText }}</view>
</view>
</view>
</uni-popup>
</div>
</template>
<script setup lang="ts">
/**
* UI 需要自定义的modal
*/
import { ref, watch } from 'vue';
const popupRef = ref()
const emit = defineEmits(['cancel', 'confirm', 'update:show'])
const props = defineProps({
show: { default: false, },
type: { default: 'center' },
title: { default: '提示' },
content: { default: '' },
confirmText: { default: '确认' },
cancelText: { default: '取消' },
confirmColor: { default: '#007aff' },
cancelColor: { default: '#000' },
width: {
type: [String, Number],
default: 600
},
height: {
type: [String, Number],
default: 350
}
})
const onCancel = () => {
emit('update:show', false)
emit('cancel')
}
const onConfirm = () => {
emit('update:show', false)
emit('confirm')
}
const showModal = () => {
popupRef.value.open(props.type)
}
const hideModal = () => {
popupRef.value.close()
}
watch(() => props.show, (val) => {
if (val) showModal()
else hideModal()
})
</script>
<style lang="scss" scoped>
.g-popup {
z-index: 101;
position: fixed;
top: 0;
left: 0;
display: flex;
}
.show-modal {
z-index: 102;
width: 630rpx;
margin: 0 30px;
padding: 50rpx;
box-sizing: border-box;
background: white;
border-radius: 28rpx;
.title {
margin-bottom: 24rpx;
text-align: center;
font-size: 40rpx;
color: #333333;
font-weight: bold;
}
.modal-content {
margin-bottom: 54rpx;
font-size: 28rpx;
font-weight: 400;
color: #666666;
line-height: 48rpx;
}
.btn-box {
.cc {
width: 252rpx;
height: 96rpx;
line-height: 96rpx;
background: #e5eeff;
text-align: center;
font-weight: 500;
font-size: 32rpx;
border-radius: 60rpx;
}
.cancel {
color: #4989fe;
}
.sure {
background: linear-gradient(270deg, #4989fe 0%, #49adfe 100%);
color: white;
}
}
}
</style>
<template>
<div class="title-bar" :style="getStyle(styles)">
<div class="title-box" :style="{ marginTop: `${store.system.statusBarHeight}px` }">
<div class="title-left" @click="back">
<div class="title-left-icon" v-if="backShow">
<img src="@/static/image/left.svg" style="width:22px;margin-top: 2px;height: 44px;" />
{{ backText }}
</div>
</div>
<div class="title-centent ellipsis">{{ title }}</div>
<div class="title-right"></div>
</div>
</div>
</template>
<script setup lang="ts">
import router from '@/router';
import store from '@/store';
import { reactive } from 'vue';
const props = defineProps({
styles: { default: {} },
title: { default: '标题' },
backText: { default: '' },
backShow: { default: true },
backFun: { default: null as any as Function },
})
const conf = reactive({
style: {
background: '#fff000',
} as CSSStyleDeclaration
})
const getStyle = (val: any = {}): any => {
Object.keys(val).forEach(key => {
conf.style[key] = val[key]
})
return conf.style
}
const back = () => {
if (props.backFun) props.backFun()
else router.back()
}
</script>
<style lang="scss" scoped>
.title {
&-bar {
position: fixed;
z-index: 101;
width: 100vw;
}
&-box {
display: flex;
height: 44px;
padding: 0 10px;
justify-content: flex-start;
align-items: center;
color: #000;
}
&-left {
width: 60px;
height: 100%;
position: relative;
display: flex;
align-items: center;
&-icon {
display: inline-flex;
align-items: center;
color: rgb(51, 51, 51);
}
}
&-centent {
flex: 1;
text-align: center;
font-size: 16px;
}
&-right {
width: 80px;
}
}
</style>
<template>
<div :class="conf.class">
<div style="margin-top:-64rpx;position: absolute;" class="log-btn column flex-center log-btn-wh" @click="show">{{
conf.show ? '隐藏' : '显示'
}}日志</div>
<div style="margin-top:-64rpx;position: absolute;right:10rpx;width: 100rpx;" class="log-btn column flex-center log-btn-wh"
@click="store.log.clear">
清空</div>
<div class="log-content">
<template v-for="(item, index) in store.log.list" :key="index">
<div>
{{ item }}
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import store from '@/store';
import { reactive } from 'vue';
const conf = reactive({
show: false,
transform: 'translateY(20vh)',
class: ['', 'log-box']
})
const show = () => {
conf.show = !conf.show
if (conf.show) conf.class.push('log-box-show')
else conf.class.pop()
}
show()
</script>
<style lang="scss">
.log-box {
z-index: 110;
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 20vh;
background: rgba(255, 255, 255, 0.616);
display: flex;
padding-bottom: env(safe-area-inset-bottom);
border-top: 1px solid #a39797;
justify-content: center;
transform: translateY(20vh);
transition: all .3s;
}
.log-box-show {
transform: translateY(0vh);
}
.log-content {
font-size: 30rpx;
height: 20vh;
width: 100vw;
overflow-y: auto;
}
.log-btn-wh {
width: 200rpx;
height: 44rpx;
border-radius: 44rpx;
padding: 5rpx 18rpx;
font-size: 30rpx;
}
.log-btn {
background: var(--primary);
color: #FFFFFF;
border: 1px solid var(--primary);
transition: all .3s;
&:active {
color: #C5C5C5;
background: none;
border: 1px solid #C5C5C5;
}
}
</style>
<template>
<div class="mask-box">
</div>
</template>
<script setup lang="ts">
</script>
<style lang="scss">
.mask-box {
z-index: 200;
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100vh;
width: 100vw;
background: rgba(255, 255, 255, 0);
}
</style>
/// <reference types="vite/client" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
declare const wx;
\ No newline at end of file
import '@/styles/index.scss'
import { createSSRApp } from 'vue'
import GlobalBottom from '@/components/global/global-bottom.vue'
import GlobalModal from '@/components/global/global-modal.vue'
import GlobalTitle from '@/components/global/global-title.vue'
import log from '@/components/log/index.vue'
import mask from '@/components/mask/index.vue'
import RouteView from '@/router/index.vue'
import App from './App.vue'
import ctimer from './utils/ctimer'
// import ElementPlus from 'element-plus'
// import 'element-plus/dist/index.css'
// import map from '@/common/map.js'
import uviewPlus from '@/uni_modules/uview-plus'
ctimer.init()
export function createApp() {
const app = createSSRApp(App)
app.component('GlobalTitle', GlobalTitle)
app.component('GlobalModal', GlobalModal)
app.component('GlobalBottom', GlobalBottom)
app.component('RouteView', RouteView)
app.component('log', log)
app.component('mask', mask)
// app.use(ElementPlus)
app.use(uviewPlus)
// app.config.globalProperties.map = map
return {
app,
}
}
\ No newline at end of file
{
"name" : "uniapp-vue3-ts-fast",
"appid" : "",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* 模块配置 */
"modules" : {},
/* 应用发布信息 */
"distribute" : {
/* android打包配置 */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios打包配置 */
"ios" : {},
/* SDK配置 */
"sdkConfigs" : {}
}
},
/* 快应用特有相关 */
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false,
"minified" : true
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3"
}
{
"easycom": {
"autoscan": true,
"custom": {
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
},
"pages": [{
"path": "pages/route/index"
}],
"globalStyle": {
"navigationStyle": "custom",
"navigationBarTextStyle": "black"
}
}
\ No newline at end of file
<template>
<div class="content column flex-center">
<div style="font-size:100rpx">404</div>
<div style="font-size:40rpx">
路径:{{ conf.param.path }}
</div>
<div style="margin-top:64rpx;width:300rpx" class="wty-btn column flex-center wty-btn-wh" @click="router.push('/load')">
重新加载</div>
</div>
</template>
<script lang="ts" setup>
import router from '@/router';
import { onMounted, reactive } from 'vue';
const conf = reactive({
param: {} as any
})
onMounted(() => {
conf.param = router.item.param
})
</script>
<style lang="less" scoped>
</style>
\ No newline at end of file
<template>
<div style="width:100vw;height:100vh;overflow: hidden;" class="row flex-center">
<div class="box row flex-center">
<img src="/static/image/bg2.png" class="icon" />
<div style="position: absolute;">
<img src="/static/image/logo.png" class="logo" />
</div>
<div class="logo_text">
Uniapp+Vue3+TypeScript
</div>
</div>
</div>
</template>
<script setup lang="ts">
import router from '@/router';
import ctimer from '@/utils/ctimer';
ctimer.add(() => {
router.clear('/login')
}, 1500)
</script>
<style lang="scss" scoped>
.box {
position: relative;
}
.icon {
width: 750rpx;
height: 1010rpx;
}
.logo {
width: 55%;
height: 100rpx;
}
.logo_text {
position: absolute;
bottom: -20rpx;
font-size: 40rpx;
font-weight: bold;
color: #85A571;
font-family: 'bsfont';
}
</style>
.box {
position: relative;
width: 90%;
}
.logo {
width: 55%;
height: 100rpx;
}
.bottom-text {
// color: #C5C5C5;
color: var(--primary);
font-size: 30rpx;
margin-top: 32rpx;
}
\ No newline at end of file
<template>
<div class="content row flex-center">
<div class="box column flex-center no-wrap">
<img src="/static/image/logo.png" class="logo" />
<u--form labelPosition="left" :rules="conf.rules" :model="conf.model1" ref="loginForm" style="width: 100%;">
<u-form-item prop="model1.username" :borderBottom="false"
style="margin-top:77rpx;width: 95%;">
<u--input v-model="conf.model1.username" placeholder="请输入账号" border="bottom" clearable />
</u-form-item>
<u-form-item prop="model1.password" :borderBottom="false"
style="margin-top:50rpx;width: 95%;">
<u--input v-model="conf.model1.password" placeholder="请输入密码" border="bottom" clearable />
</u-form-item>
</u--form>
<div style="margin-top:64rpx" class="wty-btn column flex-center wty-btn-wh" @click="loginAction">登录</div>
<!-- <div class="bottom-text" @click="changeMode">切换主题</div> -->
</div>
</div>
</template>
<script setup lang="ts">
import router from '@/router';
import store from '@/store';
import {
reactive,
onMounted,
getCurrentInstance
} from 'vue';
import {
login
} from '@/services/api/login.ts'
import {
http
} from '@/api/service.js'; // 局部引入
import {
fetchLoginToken
} from '@/api/login.ts';
import {
fetchCurrentUserInfo
} from '@/api/account.ts';
const { ctx } = getCurrentInstance()
const conf = reactive({
model1: {
username: '',
password: ''
},
theme: store.theme.theme,
rules: {
'username': {
type: 'string',
required: true,
message: '请输入账号',
trigger: ['blur', 'change']
},
'password': {
type: 'string',
required: true,
message: '请输入密码',
trigger: ['blur', 'change']
},
},
})
const loginAction = async () => {
// 获取用户列表
// http
// .get('/api/user/list')
// .then(res => {
// console.log('局部http 获取用户列表 get success----');
// console.log(res);
// })
// .catch(err => {
// console.log('局部http 获取用户列表 get fail----');
// console.log(err);
// });
const data = await fetchLoginToken(conf.model1)
if (data.data.access_token) {
uni.setStorageSync('Authorization', data.data.access_token)
}
console.log(data, `!!!!!`)
}
const formSubmit = () => {
router.pushParam('/prolist', {
id: '123'
})
}
const changeMode = async () => {
// const theme = conf.theme === 'light' ? 'dark' : 'light'
// store.theme.setTheme(theme)
// conf.theme = theme
const data = await fetchCurrentUserInfo({})
}
// onMounted(() => {
// console.log(ctx, `))))))))))`, ctx.$refs.loginForm)
// ctx.$refs.loginForm.setRules(conf.rules)
// })
</script>
<style lang="scss" scoped>
@import 'index.scss';
</style>
<template>
<div class="absolute fit column">
<div class="col scroll-y">
<template v-for="item in conf.list" :key="item.id">
<div :class="[item.id === conf.choose.id ? 'pro-show' : undefined]">
<div class="pro-line row items-center" @click="conf.choose = item">
<div class="pro-choose">
<div class="pro-choose-p">
</div>
</div>
<div class="pro-text">{{ item.name }}</div>
</div>
</div>
</template>
</div>
<div class="flex flex-center" style="height:158rpx;margin-bottom:26rpx;">
<div style="width:526rpx;margin-top:5rpx;" class="wty-btn column flex-center wty-btn-wh" @click="choose">
下一步</div>
</div>
</div>
</template>
<script lang="ts" setup>
import router from '@/router';
import { getParam } from "@/router/routeList";
import store from '@/store';
import { reactive } from 'vue';
const conf = reactive({
item: router.item.param as getParam<'/prolist'>,
list: [
{
id: 1,
name: '电商项目',
},
{
id: 2,
name: '运动项目',
},
{
id: 3,
name: '躺平项目',
},
],
choose: {
id: 1,
name: '电商项目'
}
})
const choose = () => {
store.user.proinfo = {
id: conf.choose.id,
name: conf.choose.name
}
router.clear('/service/home')
}
</script>
<style lang="scss" scoped>
.pro-line {
width: 698rpx;
height: 60rpx;
border-radius: 30rpx;
background: #EFEFEF;
margin: 14rpx auto;
color: #706460;
border: 2rpx solid #C2EDCB00;
&:nth-of-type(1) {
margin-top: 24rpx;
}
&:nth-last-of-type(1) {
margin-bottom: 24rpx;
}
}
.pro-choose {
margin-left: 6rpx;
width: 46rpx;
height: 46rpx;
border: 2rpx solid #C5C5C5;
border-radius: 50%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
&-p {
position: absolute;
width: 0rpx;
height: 0rpx;
background-color: #3EB958;
border-radius: 50%;
transition: all .3s;
transform-origin: center center;
}
}
.pro-text {
margin-left: 30rpx;
font-size: 32rpx;
&-p {
color: #3EB958;
}
}
.pro-show {
.pro-line {
background: #E7F3E9;
color: #3EB958;
border: 2rpx solid #C2EDCB;
}
.pro-choose {
border: 2rpx solid #3EB958;
}
.pro-choose-p {
width: calc(100% - 5rpx);
height: calc(100% - 5rpx);
}
}
</style>
\ No newline at end of file
<template>
<template v-if="item != null">
<global-title v-if="item.showTitle" :title="item.title ?? item.tabsText" :backText="item.titleBackText" :backFun="item.titleBackFun" :styles="item.titleStyles">
</global-title>
<view style="overflow: hidden auto;z-index: 100;position: fixed;width:100vw;background-color: #F7F7F7;" :style="{
marginTop: `${item.showTitle && item.titleTop ? store.system.statusBarHeight + store.system.uniBarHeight : 0}px`,
height: `calc(100vh - ${getHeight(item)}px)`
}">
<route-view :pagePath="item.pagePath" />
</view>
</template>
</template>
<script lang="ts" setup>
import store from '@/store';
defineProps({
transform: { default: 'translate(0vw, 0vh)' as any },
transition: { default: 'all 0s' as any },
item: { default: {} as any as routerItem },
})
const getHeight = (item: routerItem) => {
let getTitleHeight = () => {
return item.showTitle && item.titleTop ? store.system.statusBarHeight + store.system.uniBarHeight : 0
}
return getTitleHeight() + (item.showTabs ? store.system.uniTabsHeight : 0)
}
</script>
<style lang="less" scoped>
</style>
\ No newline at end of file
<template>
<view :class="['', store.theme.theme]" style="overflow: hidden;">
<page-container position="center" :duration="0" :show="store.router.show" :overlay="false"
@beforeleave="beforeleave">
</page-container>
<div style="width:200vw;height:100vh;overflow: hidden;">
<div class="router-box">
<div style="position: absolute;z-index: 100;height: 100vh;width: 100vw;" :style="{
transform: conf.router.item1.transform,
transition: conf.router.item1.transition
}">
<box :item="conf.router.item1.item" />
</div>
</div>
<div class="router-box">
<div style="position: absolute;z-index: 100;height: 100vh;width: 100vw;" :style="{
transform: conf.router.item2.transform,
transition: conf.router.item2.transition
}">
<box :item="conf.router.item2.item" />
</div>
</div>
</div>
<global-bottom :show="router.item.item.showTabs"></global-bottom>
<log v-if="conf.debug" />
<mask v-if="conf.mask" />
<!-- <fps :show="conf.fps" /> -->
</view>
</template>
<script setup lang="ts">
// import fps from '@/components/canvas/index.vue';
import router from '@/router';
import store from '@/store';
import ctimer from '@/utils/ctimer';
import { onMounted, reactive, watch } from 'vue';
import box from './box.vue';
const conf = reactive({
debug: false,
mask: false,
fps: true,
router: {
item1: {
item: null as any,
transform: 'translate(0vw, 0vh)',
transition: 'all 0s',
},
item2: {
item: null as any,
transform: 'translate(0vw, 0vh)',
transition: 'all 0s',
},
timerId: 0,
timerFun: () => { }
}
})
const ani = (val: any, item1: any, item2: any, str1: string, str2: string, str3: string) => {
// conf.mask = true
item1.transition = 'all 0s'
item1.transform = `translate(${str1}, 0vh)`
item1.item = val
ctimer.add(() => {
if (str1 !== str2)
item1.transition = 'all .3s'
item1.transform = `translate(${str2}, 0vh)`
item2.transition = 'all .3s'
item2.transform = `translate(${str3}, 0vh)`
}, 8)
conf.router.timerFun = () => {
// conf.mask = false
item2.item = null
ctimer.remove(conf.router.timerId)
conf.router.timerFun = () => { }
}
conf.router.timerId = ctimer.add(conf.router.timerFun, 300)
}
router.init()
onMounted(() => {
watch(() => router.item, (val, oldVal) => {
if (val != null) {
conf.router.timerFun()
//从左进-返回上一级路由
if (router.type === 'back') {
if (conf.router.item2.item === null) {
ani(val.item, conf.router.item2, conf.router.item1, '-200vw', '-100vw', '100vw')
} else {
ani(val.item, conf.router.item1, conf.router.item2, '-100vw', '0vw', '0vw')
}
}
//从右进-添加路由
else {
if (conf.router.item2.item === null) {
const startX = conf.router.item1.item ? '0vw' : '-100vw'
ani(val.item, conf.router.item2, conf.router.item1, startX, '-100vw', '-100vw')
} else {
ani(val.item, conf.router.item1, conf.router.item2, '100vw', '0vw', '-200vw')
}
}
}
})
//第一个页面
router.clear('/load')
})
const beforeleave = () => {
if (store.router.close) {
store.router.close = false
return
}
store.router.showRouter(() => {
router.back()
})
}
</script>
<style lang="scss" scoped>
.router-box {
display: inline-block;
width: 100vw;
height: 100vh;
}
</style>
\ No newline at end of file
<template>
<div class="absolute fit column">
<!-- <uni-swiper-dot :info="conf.info" :current="conf.current" field="content" :mode="conf.mode"
:dots-styles="conf.dotsStyles" style="margin-bottom:10rpx;">
<swiper class="swiper-box" @change="change" circular style="height:400rpx">
<swiper-item>
<img class="fit" src="@/static/image/pro.png" />
</swiper-item>
<swiper-item>
<view class="fit flex flex-center">
B
</view>
</swiper-item>
<swiper-item>
<view class="fit flex flex-center">
C
</view>
</swiper-item>
</swiper>
</uni-swiper-dot>
<div class="bottom-text" :style="{color: conf.color}" @click="clickAction">按钮</div>
<div class="col scroll-y">
<div class="row" style="gap:30rpx;padding: 30rpx;">
<div @click="clickAction" class="val-item column flex-center no-wrap" v-for="l in 20" :key="l">
<div class="val-item-text1">0.00</div>
<div class="val-item-text2">KW</div>
<div class="val-item-text3">运动距离</div>
</div>
</div>
</div> -->
<!-- :latitude="latitude" :longitude="longitude" :markers="covers" -->
<map style="width: 100%; height: 300px;"></map>
</div>
</template>
<script setup lang="ts">
import router from '@/router';
import store from '@/store';
import {
reactive,
getCurrentInstance
} from 'vue';
const internalInstance = getCurrentInstance();
const login = internalInstance.appContext.config.globalProperties.login;
const conf = reactive({
info: [1, 1, 1],
current: 0,
color: 'red',
mode: 'round',
dotsStyles: {
backgroundColor: 'rgba(66,66,66,0.1)',
border: '1px rgba(66,66,66,0) solid',
color: '#fff',
selectedBackgroundColor: 'rgba(66,66,66,0.4)',
selectedBorder: '1px rgba(66,66,66,0) solid'
},
})
router.item.item.title = store.user.proinfo.name
const change = (e: any) => {
console.log(e)
conf.current = e.detail.current;
}
const clickAction = (e) => {
console.log(`11111111111111111`, login)
conf.color = 'green'
}
</script>
<style lang="scss">
.val-item {
width: 210rpx;
height: 156rpx;
border-radius: 20rpx;
background: rgba(0, 0, 0, 0.03);
&-text1 {
font-size: 44rpx;
line-height: 44rpx;
color: #3EB958;
}
&-text2 {
font-size: 20rpx;
margin: 10rpx 0rpx 5rpx 0rpx;
color: rgba(66, 66, 66, 0.5);
}
&-text3 {
font-size: 22rpx;
color: rgba(66, 66, 66, 0.6);
}
}
</style>
<template>
<div class="absolute fit column">
<uni-swiper-dot :info="conf.info" :current="conf.current" field="content" :mode="conf.mode"
:dots-styles="conf.dotsStyles" style="margin-bottom:10rpx;">
<swiper class="swiper-box" @change="change" autoplay circular style="height:400rpx">
<swiper-item>
<img class="fit" src="@/static/image/pro.png" />
</swiper-item>
<swiper-item>
<view class="fit flex flex-center">
B
</view>
</swiper-item>
<swiper-item>
<view class="fit flex flex-center">
C
</view>
</swiper-item>
</swiper>
</uni-swiper-dot>
<div class="col scroll-y">
<div class="row" style="gap:30rpx;padding: 30rpx;">
<div class="val-item column flex-center no-wrap" v-for="l in 5" :key="l">
<div class="val-item-text1">0.00</div>
<div class="val-item-text2">KW</div>
<div class="val-item-text3">运动距离</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import router from '@/router';
import store from '@/store';
import { reactive } from 'vue';
const conf = reactive({
info: [1, 1, 1],
current: 0,
mode: 'round',
dotsStyles: {
backgroundColor: 'rgba(66,66,66,0.1)',
border: '1px rgba(66,66,66,0) solid',
color: '#fff',
selectedBackgroundColor: 'rgba(66,66,66,0.4)',
selectedBorder: '1px rgba(66,66,66,0) solid'
}
})
router.item.item.title = store.user.proinfo.name + "-摸鱼"
const change = (e: any) => {
conf.current = e.detail.current;
}
</script>
<style lang="scss">
.val-item {
width: 210rpx;
height: 156rpx;
border-radius: 20rpx;
background: rgba(0, 0, 0, 0.03);
&-text1 {
font-size: 44rpx;
line-height: 44rpx;
color: #3EB958;
}
&-text2 {
font-size: 20rpx;
margin: 10rpx 0rpx 5rpx 0rpx;
color: rgba(66, 66, 66, 0.5);
}
&-text3 {
font-size: 22rpx;
color: rgba(66, 66, 66, 0.6);
}
}
</style>
<template>
<div class="absolute fit column">
<uni-swiper-dot :info="conf.info" :current="conf.current" field="content" :mode="conf.mode"
:dots-styles="conf.dotsStyles" style="margin-bottom:10rpx;">
<swiper class="swiper-box" @change="change" autoplay circular style="height:400rpx">
<swiper-item>
<img class="fit" src="@/static/image/pro.png" />
</swiper-item>
<swiper-item>
<view class="fit flex flex-center">
B
</view>
</swiper-item>
<swiper-item>
<view class="fit flex flex-center">
C
</view>
</swiper-item>
</swiper>
</uni-swiper-dot>
<div class="col scroll-y">
<div class="row" style="gap:30rpx;padding: 30rpx;">
<div class="val-item column flex-center no-wrap" v-for="l in 10" :key="l">
<div class="val-item-text1">0.00</div>
<div class="val-item-text2">KW</div>
<div class="val-item-text3">运动距离</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import router from '@/router';
import store from '@/store';
import { reactive } from 'vue';
const conf = reactive({
info: [1, 1, 1],
current: 0,
mode: 'round',
dotsStyles: {
backgroundColor: 'rgba(66,66,66,0.1)',
border: '1px rgba(66,66,66,0) solid',
color: '#fff',
selectedBackgroundColor: 'rgba(66,66,66,0.4)',
selectedBorder: '1px rgba(66,66,66,0) solid'
}
})
router.item.item.title = store.user.proinfo.name + "-划水"
const change = (e: any) => {
conf.current = e.detail.current;
}
</script>
<style lang="scss">
.val-item {
width: 210rpx;
height: 156rpx;
border-radius: 20rpx;
background: rgba(0, 0, 0, 0.03);
&-text1 {
font-size: 44rpx;
line-height: 44rpx;
color: #3EB958;
}
&-text2 {
font-size: 20rpx;
margin: 10rpx 0rpx 5rpx 0rpx;
color: rgba(66, 66, 66, 0.5);
}
&-text3 {
font-size: 22rpx;
color: rgba(66, 66, 66, 0.6);
}
}
</style>
<template>
<div class="absolute fit column">
<uni-swiper-dot :info="conf.info" :current="conf.current" field="content" :mode="conf.mode"
:dots-styles="conf.dotsStyles" style="margin-bottom:10rpx;">
<swiper class="swiper-box" @change="change" autoplay circular style="height:400rpx">
<swiper-item>
<img class="fit" src="@/static/image/pro.png" />
</swiper-item>
<swiper-item>
<view class="fit flex flex-center">
B
</view>
</swiper-item>
<swiper-item>
<view class="fit flex flex-center">
C
</view>
</swiper-item>
</swiper>
</uni-swiper-dot>
<div class="col scroll-y">
<div class="row" style="gap:30rpx;padding: 30rpx;">
<div class="val-item column flex-center no-wrap" v-for="l in 15" :key="l">
<div class="val-item-text1">0.00</div>
<div class="val-item-text2">KW</div>
<div class="val-item-text3">运动距离</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import router from '@/router';
import store from '@/store';
import { reactive } from 'vue';
const conf = reactive({
info: [1, 1, 1],
current: 0,
mode: 'round',
dotsStyles: {
backgroundColor: 'rgba(66,66,66,0.1)',
border: '1px rgba(66,66,66,0) solid',
color: '#fff',
selectedBackgroundColor: 'rgba(66,66,66,0.4)',
selectedBorder: '1px rgba(66,66,66,0) solid'
}
})
router.item.item.title = store.user.proinfo.name + "-酱油"
const change = (e: any) => {
conf.current = e.detail.current;
}
</script>
<style lang="scss">
.val-item {
width: 210rpx;
height: 156rpx;
border-radius: 20rpx;
background: rgba(0, 0, 0, 0.03);
&-text1 {
font-size: 44rpx;
line-height: 44rpx;
color: #3EB958;
}
&-text2 {
font-size: 20rpx;
margin: 10rpx 0rpx 5rpx 0rpx;
color: rgba(66, 66, 66, 0.5);
}
&-text3 {
font-size: 22rpx;
color: rgba(66, 66, 66, 0.6);
}
}
</style>
import store from "@/store";
import { routerList } from "./routeList";
import { createRouter } from "./router";
const tabsList = [
routerList[0],
routerList[1],
routerList[2],
routerList[3],
routerList[4],
]
const router = createRouter(<any>routerList, <any>tabsList)
router.beforeJump = async (from, to, next, type) => {
next()
if (router.history.length == 2 && type === 'push') {
store.router.close = true
store.router.showRouter()
}
else if (router.history.length == 1) {
store.router.close = true
store.router.show = false
}
}
export default router
\ No newline at end of file
<template>
<error v-if="show('/error')" />
<load v-else-if="show('/load')" />
<login v-else-if="show('/login')" />
<prolist v-else-if="show('/prolist')" />
<shome v-else-if="show('/service/home')" />
<sp1 v-else-if="show('/service/p1')" />
<sp2 v-else-if="show('/service/p2')" />
<sp3 v-else-if="show('/service/p3')" />
</template>
<script lang="ts" setup>
import error from '@/pages/error/index.vue';
import load from '@/pages/load/index.vue';
import login from '@/pages/login/index.vue';
import prolist from '@/pages/prolist/index.vue';
import shome from '@/pages/service/home/index.vue';
import sp1 from '@/pages/service/p1/index.vue';
import sp2 from '@/pages/service/p2/index.vue';
import sp3 from '@/pages/service/p3/index.vue';
const props = defineProps({
pagePath: { default: '' }
})
const show = (name: string) => {
return props.pagePath === name
}
</script>
<style lang="less" scoped>
</style>
\ No newline at end of file
import router from "."
const tasObj = {
// iconPath: "/static/image/logo.png",
// selectedIconPath: "/static/image/logo.png",
showTitle: true,
titleBackText: '退出',
titleBackFun: () => {
router.clear('/login')
},
titleStyles: {
background: '#ffffff00',
color: '#0052D9'
},
showTabs: true,
}
export const routerList = [
{
pagePath: "/service/home",
tabsText: "首页",
iconPath: "/static/tabIcon/home.png",
selectedIconPath: "/static/tabIcon/home_act.png",
...tasObj,
},
{
pagePath: "/service/p1",
tabsText: "监控",
iconPath: "/static/tabIcon/monitor.png",
selectedIconPath: "/static/tabIcon/monitor_act.png",
...tasObj,
},
{
pagePath: "/service/p2",
tabsText: "工作台",
iconPath: "/static/tabIcon/workplace.png",
selectedIconPath: "/static/tabIcon/workplace_act.png",
...tasObj,
},
{
pagePath: "/service/p3",
tabsText: "消息",
iconPath: "/static/tabIcon/info.png",
selectedIconPath: "/static/tabIcon/info_act.png",
...tasObj,
},
{
pagePath: "/service/qh",
tabsText: "我的",
iconPath: "/static/tabIcon/my.png",
selectedIconPath: "/static/tabIcon/my_act.png",
// routerType: 'push',
// to: '#/prolist',
...tasObj,
},
{
pagePath: "/error",
},
{
pagePath: "/load",
},
{
pagePath: "/login",
param: {
name: null as any
},
},
{
pagePath: "/prolist",
showTitle: true,
titleTop: true,
titleStyles: {
background: 'linear-gradient(90deg, rgba(62,185,88,0.25) 0%, rgba(255,255,255,0.05) 100%)',
boxShadow: '0px 4px 20px 0px rgba(133,165,113,0.15)',
},
title: '选择项目',
param: {
id: null as any
},
},
] as const //编辑完成后使用
// ] as routerItem[]//编辑路由内容使用,起提示作用,编辑完成后请注释
type RouterList = typeof routerList
export type RouterListPath = RouterList[number]['pagePath']
type RouterListNames = UnionToTuple<RouterListPath>
//@ts-ignore
type RouterParam<T> = RouterList[IndexOf<RouterListNames, T>]['param']
export type RouterListParam<T> = RouterParam<T> extends Object ? RouterParam<T> : undefined
export type getParam<T extends RouterListPath> = RouterParam<T>;
\ No newline at end of file
import { reactive } from "vue"
import { RouterListParam as RPm, RouterListPath as RPh } from "./routeList"
const router = reactive({
//所有路由
list: [] as routerItem[],
//底部导航路由
tabslist: [] as routerItem[],
//路由历史
history: [] as routerHistory[],
//当前路由
item: null as any as routerHistory,
//操作类型
type: 'replace',
init: () => {
router.item = null as any
router.history = null as any
router.type = 'replace'
},
//保留之前路由,添加一个路由
push: (pagePath: RPh, param: any = {}) => {
if (router.item != null) if (pagePath === router.item.item.pagePath) return
const addItem = jumpItem(pagePath, param)
if (addItem) {
if (addItem.item.to) {
to(addItem.item.to, param, 'push')
return
}
const next = () => {
router.item = addItem
router.history.push(addItem)
}
router.type = 'push'
router.beforeJump(router.item, addItem, next, router.type)
} else router.toError(pagePath)
},
pushParam: <T extends RPh>(pagePath: T, param: RPm<T>) => {
router.push(pagePath, param)
},
//覆盖最后一个路由
replace: (pagePath: RPh, param: any = {}) => {
if (router.item != null) if (pagePath === router.item.item.pagePath) return
const addItem = jumpItem(pagePath, param)
if (addItem) {
if (addItem.item.to) {
to(addItem.item.to, param, 'replace')
return
}
const next = () => {
router.item = addItem
router.history[router.history.length - 1] = addItem
}
router.type = 'replace'
router.beforeJump(router.item, addItem, next, router.type)
} else router.toError(pagePath)
},
replaceParam: <T extends RPh>(pagePath: T, param: RPm<T>) => {
router.replace(pagePath, param)
},
//清空之前路由,添加一个路由
clear: (pagePath: RPh, param: any = {}) => {
if (router.item != null) if (pagePath === router.item.item.pagePath) return
const addItem = jumpItem(pagePath, param)
if (addItem) {
if (addItem.item.to) {
to(addItem.item.to, param, 'clear')
return
}
const next = () => {
router.item = addItem
router.history = [addItem]
}
router.type = 'clear'
router.beforeJump(router.item, addItem, next, router.type)
} else router.toError(pagePath)
},
clearParam: <T extends RPh>(pagePath: T, param: RPm<T>) => {
router.clear(pagePath, param)
},
back: () => {
let addItem = null as any
if (router.history.length > 1) addItem = router.history[router.history.length - 2]
const next = () => {
if (addItem == null) return
router.history.length = router.history.length - 1
router.item = addItem
}
router.type = 'back'
router.beforeJump(router.item, addItem, next, router.type)
},
beforeJump: (from: routerHistory, to: routerHistory, next: () => void, type: string) => { next() },
toError: (path: string) => {
router.push('/error', {
path
})
}
})
const jumpItem = (pagePath: string, param: any): routerHistory => {
const item = getItem(pagePath)
param = getParam(pagePath, param)
let addItem = null as any
if (item) addItem = { item: item, param: param }
return addItem
}
const getItem = (pagePath: string) => {
if (pagePath.indexOf('?') != -1) pagePath = pagePath.split('?')[0]
const arr = router.list.filter(item => item.pagePath === pagePath)
if (arr.length > 0) return arr[0]
return null
}
const getParam = (pagePath: string, param: any) => {
if (pagePath.indexOf('?') != -1) {
pagePath.split('?')[1].split('&').forEach(paramTemp => {
const p = paramTemp.split('=')
param[p[0]] = p[1]
})
}
return param
}
const to = (toPath: any, param: any, type: string) => {
if (toPath.indexOf('#') != -1)
router[type](toPath.substring(1), param)
else router.toError(toPath+"-前应添加#")
}
export const createRouter = (list: routerItem[], tabsList: routerItem[]) => {
router.list = list
router.tabslist = tabsList
return router
}
\ No newline at end of file
declare type routerItem = {
pagePath: string;
//跳转-需要在路径前加上#-如:跳转到'/home',写成'#/home'
to?: string
//顶部导航栏配置
title?: string;
titleTop?: boolean
showTitle?: boolean
titleBackText?: string
titleBackFun?: string
titleStyles?: CSSStyleDeclaration
//底部导航栏配置
showTabs?: boolean
iconPath?: string;
selectedIconPath?: string;
tabsText?: string;
routerType?: string //点击跳转方式
}
declare type routerHistory = {
item: routerItem,
param?: {
[key: string]: any
};
}
\ No newline at end of file
import axios from '../request.ts';
namespace Login {
// 用户登录表单
export interface LoginReqForm {
  username: string;
  password: string;
}
// 登录成功后返回的token
export interface LoginResData {
  token: string;
}
}
// 用户登录
const login = (params: Login.LoginReqForm) => {
// 返回的数据格式可以和服务端约定
return axios.post<Login.LoginResData>('/user/login', params);
}
export {
login
}
\ No newline at end of file
import axios, {AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse} from 'axios'
// import {ElMessage} from 'element-plus'
// 数据返回的接口
// 定义请求响应参数,不含data
interface Result {
code: number;
msg: string
}
// 请求响应参数,包含data
interface ResultData<T = any> extends Result {
data?: T;
}
const URL: string = ''
enum RequestEnums {
TIMEOUT = 20000,
OVERDUE = 600, // 登录失效
FAIL = 999, // 请求失败
SUCCESS = 200, // 请求成功
}
const config = {
// 默认地址
baseURL: URL as string,
// 设置超时时间
timeout: RequestEnums.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: true
}
class RequestHttp {
// 定义成员变量并指定类型
service: AxiosInstance;
public constructor(config: AxiosRequestConfig) {
  // 实例化axios
  this.service = axios.create(config);
  /**
    * 请求拦截器
    * 客户端发送请求 -> [请求拦截器] -> 服务器
    * token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
    */
  this.service.interceptors.request.use(
    (config: AxiosRequestConfig) => {
      const token = localStorage.getItem('token') || '';
      return {
        ...config,
        headers: {
          'x-access-token': token, // 请求头中携带token信息
        }
      }
    },
    (error: AxiosError) => {
      // 请求报错
      Promise.reject(error)
    }
  )
  /**
    * 响应拦截器
    * 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
    */
  this.service.interceptors.response.use(
    (response: AxiosResponse) => {
      const {data, config} = response; // 解构
      if (data.code === RequestEnums.OVERDUE) {
        // 登录信息失效,应跳转到登录页面,并清空本地的token
        localStorage.setItem('token', '');
        // router.replace({
        //   path: '/login'
        // })
        return Promise.reject(data);
      }
      // 全局错误信息拦截(防止下载文件得时候返回数据流,没有code,直接报错)
      if (data.code && data.code !== RequestEnums.SUCCESS) {
//         ElMessage.error(data); // 此处也可以使用组件提示报错信息
        return Promise.reject(data)
      }
      return data;
    },
    (error: AxiosError) => {
      const {response} = error;
      if (response) {
        this.handleCode(response.status)
      }
      if (!window.navigator.onLine) {
//         ElMessage.error('网络连接失败');
        // 可以跳转到错误页面,也可以不做操作
        // return router.replace({
        //   path: '/404'
        // });
      }
    }
  )
}
handleCode(code: number):void {
  switch(code) {
    case 401:
console.log(`111111111111`)
//       ElMessage.error('登录失败,请重新登录');
      break;
    default:
console.log(`111111111111`)
//       ElMessage.error('请求失败');
      break;
  }
}
// 常用方法封装
get<T>(url: string, params?: object): Promise<ResultData<T>> {
  return this.service.get(url, {params});
}
post<T>(url: string, params?: object): Promise<ResultData<T>> {
  return this.service.post(url, params);
}
put<T>(url: string, params?: object): Promise<ResultData<T>> {
  return this.service.put(url, params);
}
delete<T>(url: string, params?: object): Promise<ResultData<T>> {
  return this.service.delete(url, {params});
}
patch<T>(url: string, params?: object): Promise<ResultData<T>> {
  return this.service.patch(url, {params});
}
}
// 导出一个实例对象
export default new RequestHttp(config);
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1663692239032" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2999" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M431.36 488.41L704 811.78l-53.75999999 62.88L377.6 555.78 320 492.9 373.76 430 650.24000001 106.66 704 169.53z" p-id="3000"></path></svg>
\ No newline at end of file
import { reactive } from "vue"
import store from "."
const ctx = reactive({
timer: null as any,
map: new Map<number, { fun: () => void, id: number }>(),
add: (fun: () => void) => {
const id = store.timer.getId()
ctx.map.set(id, {
fun,
id,
})
return id
},
run: () => {
ctx.map.forEach(item => {
item.fun()
store.ctx.remove(item.id)
})
},
remove: (id: number) => {
ctx.map.delete(id)
}
})
export default ctx
\ No newline at end of file
import router from './router'
import system from './system'
import theme from './theme'
import user from './user'
import log from './log'
import timer from './timer'
import ctx from './ctx'
export default class store {
static router = router
static system = system
static theme = theme
static user = user
static log = log
static timer = timer
static ctx = ctx
}
\ No newline at end of file
import { reactive } from "vue"
const log = reactive({
list: [] as string[],
clear: () => {
log.list = []
}
})
export default log
\ No newline at end of file
import ctimer from "@/utils/ctimer"
import { reactive } from "vue"
const router = reactive({
show: false,
close: false,
showRouter: (callback: () => void = () => { }) => {
router.show = false
ctimer.add(() => {
router.show = true
router.close = false
callback()
}, 1)
}
})
export default router
\ No newline at end of file
import { reactive } from "vue"
let isWEIXIN = false
let isH5 = false
// #ifdef MP-WEIXIN
isWEIXIN = true
// #endif
// #ifdef H5
isH5 = true
// #endif
/**
* 判断是否为微信浏览器环境
*/
const isWeixinClient = () => {
let isWeixinClient = false
// #ifdef H5
isWeixinClient = window.navigator.userAgent.toLowerCase().indexOf('micromessenger') !== -1
// #endif
return isWeixinClient
}
const systemInfo = uni.getSystemInfoSync()
const system = reactive({
uniBarHeight: 44,
uniTabsHeight: 70,
statusBarHeight: systemInfo.statusBarHeight as number,
windowHeight: systemInfo.windowHeight,
windowWidth: systemInfo.windowWidth,
pixelRatio: systemInfo.pixelRatio,//分辨率比值
platform: systemInfo.platform, // 客户端平台,值域为:ios、android
system: systemInfo.system, // 操作系统版本
brand: systemInfo.brand, // 手机品牌
model: systemInfo.model, // 手机型号
isAndroid: systemInfo.system.includes('Android') || systemInfo.system.includes('Other'),
isIOS: systemInfo.system.includes('iOS'),
isDev: process.env.NODE_ENV === 'development',
isH5,
isWEIXIN,
isWeixinClient: isWeixinClient(),
})
export default system
\ No newline at end of file
import cookie from "@/utils/cookie"
import { reactive } from "vue"
const themeCache = cookie.get('theme')
const themeList = ['light', 'dark']
const theme = reactive({
theme: themeList.includes(themeCache) ? themeCache : 'light',
setTheme: (name: string) => {
if (themeList.includes(name)) {
theme.theme = name
cookie.set('theme', name)
}
}
})
export default theme
\ No newline at end of file
import { reactive } from "vue"
const timer = reactive({
id: 1000,
getId: () => {
timer.id++
return timer.id
},
timer: null as any,
map: new Map<number, { endTime: number, fun: () => void, id: number }>(),
add: (fun: () => void, endTime: number) => {
const id = timer.getId()
timer.map.set(id, {
endTime: new Date().getTime() + endTime,
fun,
id,
})
return id
},
remove: (id: number) => {
timer.map.delete(id)
}
})
export default timer
\ No newline at end of file
import { reactive } from "vue"
const user = reactive({
proinfo: {
id: 0,
name: ''
}
})
export default user
\ No newline at end of file
@import 'com/btn.scss';
@import 'com/input.scss';
\ No newline at end of file
.wty-btn-wh {
width: 526rpx;
height: 88rpx;
border-radius: 88rpx;
padding: 5rpx 36rpx;
font-size: 40rpx;
}
.wty-btn {
background: var(--primary);
color: #FFFFFF;
border: 1px solid var(--primary);
transition: all .3s;
&:active {
color: #C5C5C5;
background: none;
border: 1px solid #C5C5C5;
}
}
\ No newline at end of file
.wty-input {
color: #0658DF;
background: #FFFFFF;
border: 1px solid #FFFFFF;
&:active,
&:focus,
&:hover {
border: 1px solid #0658DF33;
}
}
\ No newline at end of file
This diff is collapsed.
@import 'com.scss';
@import 'theme/index.scss';
.content {
position: fixed;
height: 100%;
width: 100%;
background: #F7F7F7;
}
\ No newline at end of file
@font-face {
font-family: 'bsfont';
src: url('@/static/font/font.ttf') format('truetype');
}
\ No newline at end of file
@import 'font';
@import 'common';
@import 'core';
\ No newline at end of file
.dark {
--primary: #007bff;
}
\ No newline at end of file
@import 'light.scss';
@import 'dark.scss';
\ No newline at end of file
.light {
--primary: #6EDB85;
}
\ No newline at end of file
//默认动画
$base-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), border 0s,
background 0s, color 0s, font-size 0s;
//默认动画长
$base-transition-time: 0.3s;
$base-transition-time-4: 0.4s;
$base-color: #f45;
$green-color: #11d86c;
type UnionToFunc<T> = T extends unknown ? (arg: T) => void : never
type UnionToIntersection<U> = UnionToFunc<U> extends (arg: infer Arg) => void ? Arg : never
type LastInUnion<U> = UnionToIntersection<UnionToFunc<U>> extends (x: infer L) => void ? L : never
declare type UnionToTuple<T, L = LastInUnion<T>> = [L] extends [never] ? [] : [...UnionToTuple<Exclude<T, L>>, L]
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false
declare type IndexOf<T extends unknown[], U extends unknown, Count extends 1[] = []> =
T extends [infer First, ...infer Rest] ? (
Equal<First, U> extends true ? Count['length'] : IndexOf<Rest, U, [...Count, 1]>
) : -1
\ No newline at end of file
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
@import '@/uni_modules/uview-plus/theme.scss';
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f8f8f8;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:24rpx;
$uni-font-size-base:28rpx;
$uni-font-size-lg:32rpx;
/* 图片尺寸 */
$uni-img-size-sm:40rpx;
$uni-img-size-base:52rpx;
$uni-img-size-lg:80rpx;
/* Border Radius */
$uni-border-radius-sm: 4rpx;
$uni-border-radius-base: 6rpx;
$uni-border-radius-lg: 12rpx;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 10px;
$uni-spacing-row-base: 20rpx;
$uni-spacing-row-lg: 30rpx;
/* 垂直间距 */
$uni-spacing-col-sm: 8rpx;
$uni-spacing-col-base: 16rpx;
$uni-spacing-col-lg: 24rpx;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:40rpx;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:36rpx;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:30rpx;
\ No newline at end of file
MIT License
Copyright (c) 2020 www.uviewui.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
<p align="center">
<img alt="logo" src="https://uviewui.com/common/logo.png" width="120" height="120" style="margin-bottom: 10px;">
</p>
<h3 align="center" style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uview-plus 3.0</h3>
<h3 align="center">多平台快速开发的UI框架</h3>
[![stars](https://img.shields.io/github/stars/ijry/uview-plus?style=flat-square&logo=GitHub)](https://github.com/ijry/uview-plus)
[![forks](https://img.shields.io/github/forks/ijry/uview-plus?style=flat-square&logo=GitHub)](https://github.com/ijry/uview-plus)
[![issues](https://img.shields.io/github/issues/ijry/uview-plus?style=flat-square&logo=GitHub)](https://github.com/ijry/uview-plus/issues)
[![release](https://img.shields.io/github/v/release/ijry/uview-plus?style=flat-square)](https://gitee.com/uiadmin/uview-plus/releases)
[![license](https://img.shields.io/github/license/ijry/uview-plus?style=flat-square)](https://en.wikipedia.org/wiki/MIT_License)
## 说明
uview-plus,是uni-app全面兼容vue3/nvue的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水。uview-plus是基于uView2.x移植的支持vue3的版本,感谢uView。
## [官方文档:https://uiadmin.net/uview-plus](https://uiadmin.net/uview-plus)
## 预览
您可以通过**微信**扫码,查看最佳的演示效果。
<br>
<br>
<img src="https://uiadmin.net/uview-plus/common/h5_qrcode.png" width="220" height="220" >
## 链接
- [官方文档](https://uiadmin.net/uview-plus)
- [更新日志](https://uiadmin.net/uview-plus/components/changelog.html)
- [升级指南](https://uiadmin.net/uview-plus/components/changeGuide.html)
- [关于我们](https://uiadmin.net/uview-plus/cooperation/about.html)
## 交流反馈
欢迎加入我们的QQ群交流反馈:[点此跳转](https://uiadmin.net/uview-plus/components/addQQGroup.html)
## 关于PR
> 我们非常乐意接受各位的优质PR,但在此之前我希望您了解uview-plus是一个需要兼容多个平台的(小程序、h5、ios app、android app)包括nvue页面、vue页面。
> 所以希望在您修复bug并提交之前尽可能的去这些平台测试一下兼容性。最好能携带测试截图以方便审核。非常感谢!
## 安装
#### **uni-app插件市场链接** —— [https://ext.dcloud.net.cn/plugin?name=uview-plus](https://ext.dcloud.net.cn/plugin?name=uview-plus)
请通过[官网安装文档](https://uiadmin.net/uview-plus/components/install.html)了解更详细的内容
## 快速上手
请通过[快速上手](https://uiadmin.net/uview-plus/components/quickstart.html)了解更详细的内容
## 使用方法
配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。
```html
<template>
<u-button text="按钮"></u-button>
</template>
```
## 版权信息
uview-plus遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uview-plus应用到您的产品中。
## 3.1.18(2022-10-23)
新增页面模板
## 3.1.17(2022-10-23)
演示项目增加国家化支持及多个页面模板
## 3.1.16(2022-10-21)
修复u-switch、u-picker等
## 3.1.15(2022-10-13)
修复:数据更新后,上传组件的视图没有更新
复选框 超出不换行
单选框 超出不换行
## 3.1.14(2022-10-11)
swipe-action-item组件增加emits定义
## 3.1.13(2022-09-19)
修复富文本解析
## 3.1.12(2022-09-15)
check-box组件清空数组时无法更新视图的问题
## 3.1.11(2022-09-14)
修复u-checkbox-group等
## 3.1.10(2022-09-14)
修复u-checkbox-box双向绑定
## 3.1.9(2022-09-08)
紧急修复缺失endif
## 3.1.8(2022-09-07)
修复u-checkbox-group双向绑定
修复picker缺少emits定义
## 3.1.7(2022-08-28)
修复布局在微信小程序失效
## 3.1.6(2022-08-25)
修复微信小程序下tabbar样式问题
## 3.1.5(2022-08-24)
修复icon/text/tabs点击事件执行两次
## 3.1.4(2022-08-20)
修复button组件点击事件执行两次
## 3.1.3(2022-08-11)
修复小程序报错Generated an empty chunk,修复示例工程引入request参数配置无效。
## 3.1.2(2022-08-09)
修复示例项目对request的调用引入
## 3.1.1(2022-08-07)
折叠面板在小程序会报'setContentAnimate' of undefined
## 3.1.0(2022-08-07)
初步解决App端运行时提示props未定义且白屏问题
## 3.0.17(2022-08-03)
修复u-image组件报borderRadius警告
## 3.0.16(2022-07-30)
修复方法重复,属性重复等多个编译到APP时的warning
## 3.0.15(2022-07-22)
修复支付宝下不支持this.$slots导致cell的slot失效
## 3.0.14(2022-07-22)
修复u-grid-item与u-swipe-action-item在支付宝编译报错问题
## 3.0.13(2022-07-22)
修复u--input与u--textarea组件多个事件执行两次问题
## 3.0.12(2022-07-20)
修复u-action-sheet中v-for报错
u-collapse-item中slot修复
## 3.0.11(2022-07-19)
修复微信小程序编译报错及其它修复
## 3.0.10(2022-07-15)
修复文档一批slot示例写法
修复u-search缺失emits定义导致warning
## 3.0.9(2022-07-14)
修复u-search双向绑定
## 3.0.8(2022-07-12)
修复u-tag默认宽度撑满容器
## 3.0.7(2022-07-12)
修复u-navbar自定义插槽演示示例
## 3.0.6(2022-07-11)
修复u-image缺少emits申明
## 3.0.5(2022-07-11)
修复u-upload缺少emits申明
## 3.0.4(2022-07-10)
修复u-textarea/u-input/u-datetime-picker/u-number-box/u-radio-group/u-switch/u-rate在vue3下数据绑定
## 3.0.3(2022-07-09)
启用自建演示二维码
## 3.0.2(2022-07-09)
修复dayjs/clipboard等导致打包报错
## 3.0.1(2022-07-09)
增加插件市场地址
## 3.0.0(2022-07-09)
# uview-plus(vue3)初步发布
<template>
<uvForm
ref="uForm"
:model="model"
:rules="rules"
:errorType="errorType"
:borderBottom="borderBottom"
:labelPosition="labelPosition"
:labelWidth="labelWidth"
:labelAlign="labelAlign"
:labelStyle="labelStyle"
:customStyle="customStyle"
>
<slot />
</uvForm>
</template>
<script>
/**
* 此组件存在的理由是,在nvue下,u-form被uni-app官方占用了,u-form在nvue中相当于form组件
* 所以在nvue下,取名为u--form,内部其实还是u-form.vue,只不过做一层中转
*/
import uvForm from '../u-form/u-form.vue';
import props from '../u-form/props.js';
import mpMixin from '../../libs/mixin/mpMixin.js';
import mixin from '../../libs/mixin/mixin.js';
export default {
// #ifdef MP-WEIXIN
name: 'u-form',
// #endif
// #ifndef MP-WEIXIN
name: 'u--form',
// #endif
mixins: [mpMixin, props, mixin],
components: {
uvForm
},
created() {
this.children = []
},
methods: {
// 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则
setRules(rules) {
this.$refs.uForm.setRules(rules)
},
validate() {
/**
* 在微信小程序中,通过this.$parent拿到的父组件是u--form,而不是其内嵌的u-form
* 导致在u-form组件中,拿不到对应的children数组,从而校验无效,所以这里每次调用u-form组件中的
* 对应方法的时候,在小程序中都先将u--form的children赋值给u-form中的children
*/
// #ifdef MP-WEIXIN
this.setMpData()
// #endif
return this.$refs.uForm.validate()
},
validateField(value, callback) {
// #ifdef MP-WEIXIN
this.setMpData()
// #endif
return this.$refs.uForm.validateField(value, callback)
},
resetFields() {
// #ifdef MP-WEIXIN
this.setMpData()
// #endif
return this.$refs.uForm.resetFields()
},
clearValidate(props) {
// #ifdef MP-WEIXIN
this.setMpData()
// #endif
return this.$refs.uForm.clearValidate(props)
},
setMpData() {
this.$refs.uForm.children = this.children
}
},
}
</script>
<template>
<uvImage
:src="src"
:mode="mode"
:width="width"
:height="height"
:shape="shape"
:radius="radius"
:lazyLoad="lazyLoad"
:showMenuByLongpress="showMenuByLongpress"
:loadingIcon="loadingIcon"
:errorIcon="errorIcon"
:showLoading="showLoading"
:showError="showError"
:fade="fade"
:webp="webp"
:duration="duration"
:bgColor="bgColor"
:customStyle="customStyle"
@click="$emit('click')"
@error="$emit('error')"
@load="$emit('load')"
>
<template v-slot:loading>
<slot name="loading"></slot>
</template>
<template v-slot:error>
<slot name="error"></slot>
</template>
</uvImage>
</template>
<script>
/**
* 此组件存在的理由是,在nvue下,u-image被uni-app官方占用了,u-image在nvue中相当于image组件
* 所以在nvue下,取名为u--image,内部其实还是u-iamge.vue,只不过做一层中转
*/
import uvImage from '../u-image/u-image.vue';
import props from '../u-image/props.js';
import mpMixin from '../../libs/mixin/mpMixin.js';
import mixin from '../../libs/mixin/mixin.js';
export default {
name: 'u--image',
mixins: [mpMixin, props, mixin],
components: {
uvImage
},
emits: ['click', 'error', 'load']
}
</script>
\ No newline at end of file
<template>
<uvInput
:value="value"
:modelValue="modelValue"
:type="type"
:fixed="fixed"
:disabled="disabled"
:disabledColor="disabledColor"
:clearable="clearable"
:password="password"
:maxlength="maxlength"
:placeholder="placeholder"
:placeholderClass="placeholderClass"
:placeholderStyle="placeholderStyle"
:showWordLimit="showWordLimit"
:confirmType="confirmType"
:confirmHold="confirmHold"
:holdKeyboard="holdKeyboard"
:focus="focus"
:autoBlur="autoBlur"
:disableDefaultPadding="disableDefaultPadding"
:cursor="cursor"
:cursorSpacing="cursorSpacing"
:selectionStart="selectionStart"
:selectionEnd="selectionEnd"
:adjustPosition="adjustPosition"
:inputAlign="inputAlign"
:fontSize="fontSize"
:color="color"
:prefixIcon="prefixIcon"
:suffixIcon="suffixIcon"
:suffixIconStyle="suffixIconStyle"
:prefixIconStyle="prefixIconStyle"
:border="border"
:readonly="readonly"
:shape="shape"
:customStyle="customStyle"
:formatter="formatter"
:ignoreCompositionEvent="ignoreCompositionEvent"
@input="e => $emit('input', e)"
@update:modelValue="e => $emit('update:modelValue', e)"
>
<!-- #ifdef MP -->
<slot name="prefix"></slot>
<slot name="suffix"></slot>
<!-- #endif -->
<!-- #ifndef MP -->
<slot name="prefix" slot="prefix"></slot>
<slot name="suffix" slot="suffix"></slot>
<!-- #endif -->
</uvInput>
</template>
<script>
/**
* 此组件存在的理由是,在nvue下,u-input被uni-app官方占用了,u-input在nvue中相当于input组件
* 所以在nvue下,取名为u--input,内部其实还是u-input.vue,只不过做一层中转
*/
import uvInput from '../u-input/u-input.vue';
import props from '../u-input/props.js';
import mpMixin from '../../libs/mixin/mpMixin.js';
import mixin from '../../libs/mixin/mixin.js';
export default {
name: 'u--input',
mixins: [mpMixin, props, mixin],
components: {
uvInput
},
}
</script>
\ No newline at end of file
<template>
<uvText
:type="type"
:show="show"
:text="text"
:prefixIcon="prefixIcon"
:suffixIcon="suffixIcon"
:mode="mode"
:href="href"
:format="format"
:call="call"
:openType="openType"
:bold="bold"
:block="block"
:lines="lines"
:color="color"
:decoration="decoration"
:size="size"
:iconStyle="iconStyle"
:margin="margin"
:lineHeight="lineHeight"
:align="align"
:wordWrap="wordWrap"
:customStyle="customStyle"
></uvText>
</template>
<script>
/**
* 此组件存在的理由是,在nvue下,u-text被uni-app官方占用了,u-text在nvue中相当于input组件
* 所以在nvue下,取名为u--input,内部其实还是u-text.vue,只不过做一层中转
* 不使用v-bind="$attrs",而是分开独立写传参,是因为微信小程序不支持此写法
*/
import uvText from "../u-text/u-text.vue";
import props from "../u-text/props.js";
import mpMixin from '../../libs/mixin/mpMixin.js'
import mixin from '../../libs/mixin/mixin.js'
export default {
name: "u--text",
mixins: [mpMixin, mixin, props,],
components: {
uvText,
},
};
</script>
<template>
<uvTextarea
:value="value"
:modelValue="modelValue"
:placeholder="placeholder"
:height="height"
:confirmType="confirmType"
:disabled="disabled"
:count="count"
:focus="focus"
:autoHeight="autoHeight"
:fixed="fixed"
:cursorSpacing="cursorSpacing"
:cursor="cursor"
:showConfirmBar="showConfirmBar"
:selectionStart="selectionStart"
:selectionEnd="selectionEnd"
:adjustPosition="adjustPosition"
:disableDefaultPadding="disableDefaultPadding"
:holdKeyboard="holdKeyboard"
:maxlength="maxlength"
:border="border"
:customStyle="customStyle"
:formatter="formatter"
:ignoreCompositionEvent="ignoreCompositionEvent"
@input="e => $emit('input', e)"
@update:modelValue="e => $emit('update:modelValue', e)"
></uvTextarea>
</template>
<script>
/**
* 此组件存在的理由是,在nvue下,u--textarea被uni-app官方占用了,u-textarea在nvue中相当于textarea组件
* 所以在nvue下,取名为u--textarea,内部其实还是u-textarea.vue,只不过做一层中转
*/
import uvTextarea from '../u-textarea/u-textarea.vue';
import props from '../u-textarea/props.js';
import mpMixin from '../../libs/mixin/mpMixin.js';
import mixin from '../../libs/mixin/mixin.js';
export default {
name: 'u--textarea',
mixins: [mpMixin, props, mixin],
components: {
uvTextarea
},
}
</script>
import defprops from '../../libs/config/props';
export default {
props: {
// 操作菜单是否展示 (默认false)
show: {
type: Boolean,
default: defprops.actionSheet.show
},
// 标题
title: {
type: String,
default: defprops.actionSheet.title
},
// 选项上方的描述信息
description: {
type: String,
default: defprops.actionSheet.description
},
// 数据
actions: {
type: Array,
default: defprops.actionSheet.actions
},
// 取消按钮的文字,不为空时显示按钮
cancelText: {
type: String,
default: defprops.actionSheet.cancelText
},
// 点击某个菜单项时是否关闭弹窗
closeOnClickAction: {
type: Boolean,
default: defprops.actionSheet.closeOnClickAction
},
// 处理底部安全区(默认true)
safeAreaInsetBottom: {
type: Boolean,
default: defprops.actionSheet.safeAreaInsetBottom
},
// 小程序的打开方式
openType: {
type: String,
default: defprops.actionSheet.openType
},
// 点击遮罩是否允许关闭 (默认true)
closeOnClickOverlay: {
type: Boolean,
default: defprops.actionSheet.closeOnClickOverlay
},
// 圆角值
round: {
type: [Boolean, String, Number],
default: defprops.actionSheet.round
}
}
}
<template>
<u-popup
:show="show"
mode="bottom"
@close="closeHandler"
:safeAreaInsetBottom="safeAreaInsetBottom"
:round="round"
>
<view class="u-action-sheet">
<view
class="u-action-sheet__header"
v-if="title"
>
<text class="u-action-sheet__header__title u-line-1">{{title}}</text>
<view
class="u-action-sheet__header__icon-wrap"
@tap.stop="cancel"
>
<u-icon
name="close"
size="17"
color="#c8c9cc"
bold
></u-icon>
</view>
</view>
<text
class="u-action-sheet__description"
:style="[{
marginTop: `${title && description ? 0 : '18px'}`
}]"
v-if="description"
>{{description}}</text>
<slot>
<u-line v-if="description"></u-line>
<view class="u-action-sheet__item-wrap">
<view :key="index" v-for="(item, index) in actions">
<!-- #ifdef MP -->
<button
class="u-reset-button"
:openType="item.openType"
@getuserinfo="onGetUserInfo"
@contact="onContact"
@getphonenumber="onGetPhoneNumber"
@error="onError"
@launchapp="onLaunchApp"
@opensetting="onOpenSetting"
:lang="lang"
:session-from="sessionFrom"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
:app-parameter="appParameter"
@tap="selectHandler(index)"
:hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
>
<!-- #endif -->
<view
class="u-action-sheet__item-wrap__item"
@tap.stop="selectHandler(index)"
:hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
:hover-stay-time="150"
>
<template v-if="!item.loading">
<text
class="u-action-sheet__item-wrap__item__name"
:style="[itemStyle(index)]"
>{{ item.name }}</text>
<text
v-if="item.subname"
class="u-action-sheet__item-wrap__item__subname"
>{{ item.subname }}</text>
</template>
<u-loading-icon
v-else
custom-class="van-action-sheet__loading"
size="18"
mode="circle"
/>
</view>
<!-- #ifdef MP -->
</button>
<!-- #endif -->
<u-line v-if="index !== actions.length - 1"></u-line>
</view>
</view>
</slot>
<u-gap
bgColor="#eaeaec"
height="6"
v-if="cancelText"
></u-gap>
<view hover-class="u-action-sheet--hover">
<text
@touchmove.stop.prevent
:hover-stay-time="150"
v-if="cancelText"
class="u-action-sheet__cancel-text"
@tap="cancel"
>{{cancelText}}</text>
</view>
</view>
</u-popup>
</template>
<script>
import openType from '../../libs/mixin/openType'
import button from '../../libs/mixin/button'
import props from './props.js';
import mpMixin from '../../libs/mixin/mpMixin.js';
import mixin from '../../libs/mixin/mixin.js';
/**
* ActionSheet 操作菜单
* @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
* @tutorial https://www.uviewui.com/components/actionSheet.html
*
* @property {Boolean} show 操作菜单是否展示 (默认 false )
* @property {String} title 操作菜单标题
* @property {String} description 选项上方的描述信息
* @property {Array<Object>} actions 按钮的文字数组,见官方文档示例
* @property {String} cancelText 取消按钮的提示文字,不为空时显示按钮
* @property {Boolean} closeOnClickAction 点击某个菜单项时是否关闭弹窗 (默认 true )
* @property {Boolean} safeAreaInsetBottom 处理底部安全区 (默认 true )
* @property {String} openType 小程序的打开方式 (contact | launchApp | getUserInfo | openSetting |getPhoneNumber |error )
* @property {Boolean} closeOnClickOverlay 点击遮罩是否允许关闭 (默认 true )
* @property {Number|String} round 圆角值,默认无圆角 (默认 0 )
* @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文
* @property {String} sessionFrom 会话来源,openType="contact"时有效
* @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效
* @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效
* @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效
* @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效 (默认 false )
* @property {String} appParameter 打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效
*
* @event {Function} select 点击ActionSheet列表项时触发
* @event {Function} close 点击取消按钮时触发
* @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效
* @event {Function} contact 客服消息回调,openType="contact"时有效
* @event {Function} getphonenumber 获取用户手机号回调,openType="getPhoneNumber"时有效
* @event {Function} error 当使用开放能力时,发生错误的回调,openType="error"时有效
* @event {Function} launchapp 打开 APP 成功的回调,openType="launchApp"时有效
* @event {Function} opensetting 在打开授权设置页后回调,openType="openSetting"时有效
* @example <u-action-sheet :actions="list" :title="title" :show="show"></u-action-sheet>
*/
export default {
name: "u-action-sheet",
// 一些props参数和methods方法,通过mixin混入,因为其他文件也会用到
mixins: [openType, button, mixin, props],
data() {
return {
}
},
computed: {
// 操作项目的样式
itemStyle() {
return (index) => {
let style = {};
if (this.actions[index].color) style.color = this.actions[index].color
if (this.actions[index].fontSize) style.fontSize = uni.$u.addUnit(this.actions[index].fontSize)
// 选项被禁用的样式
if (this.actions[index].disabled) style.color = '#c0c4cc'
return style;
}
},
},
methods: {
closeHandler() {
// 允许点击遮罩关闭时,才发出close事件
if(this.closeOnClickOverlay) {
this.$emit('close')
}
},
// 点击取消按钮
cancel() {
this.$emit('close')
},
selectHandler(index) {
const item = this.actions[index]
if (item && !item.disabled && !item.loading) {
this.$emit('select', item)
if (this.closeOnClickAction) {
this.$emit('close')
}
}
},
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
$u-action-sheet-reset-button-width:100% !default;
$u-action-sheet-title-font-size: 16px !default;
$u-action-sheet-title-padding: 12px 30px !default;
$u-action-sheet-title-color: $u-main-color !default;
$u-action-sheet-header-icon-wrap-right:15px !default;
$u-action-sheet-header-icon-wrap-top:15px !default;
$u-action-sheet-description-font-size:13px !default;
$u-action-sheet-description-color:14px !default;
$u-action-sheet-description-margin: 18px 15px !default;
$u-action-sheet-item-wrap-item-padding:15px !default;
$u-action-sheet-item-wrap-name-font-size:16px !default;
$u-action-sheet-item-wrap-subname-font-size:13px !default;
$u-action-sheet-item-wrap-subname-color: #c0c4cc !default;
$u-action-sheet-item-wrap-subname-margin-top:10px !default;
$u-action-sheet-cancel-text-font-size:16px !default;
$u-action-sheet-cancel-text-color:$u-content-color !default;
$u-action-sheet-cancel-text-font-size:15px !default;
$u-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
.u-reset-button {
width: $u-action-sheet-reset-button-width;
}
.u-action-sheet {
text-align: center;
&__header {
position: relative;
padding: $u-action-sheet-title-padding;
&__title {
font-size: $u-action-sheet-title-font-size;
color: $u-action-sheet-title-color;
font-weight: bold;
text-align: center;
}
&__icon-wrap {
position: absolute;
right: $u-action-sheet-header-icon-wrap-right;
top: $u-action-sheet-header-icon-wrap-top;
}
}
&__description {
font-size: $u-action-sheet-description-font-size;
color: $u-tips-color;
margin: $u-action-sheet-description-margin;
text-align: center;
}
&__item-wrap {
&__item {
padding: $u-action-sheet-item-wrap-item-padding;
@include flex;
align-items: center;
justify-content: center;
flex-direction: column;
&__name {
font-size: $u-action-sheet-item-wrap-name-font-size;
color: $u-main-color;
text-align: center;
}
&__subname {
font-size: $u-action-sheet-item-wrap-subname-font-size;
color: $u-action-sheet-item-wrap-subname-color;
margin-top: $u-action-sheet-item-wrap-subname-margin-top;
text-align: center;
}
}
}
&__cancel-text {
font-size: $u-action-sheet-cancel-text-font-size;
color: $u-action-sheet-cancel-text-color;
text-align: center;
padding: $u-action-sheet-cancel-text-font-size;
}
&--hover {
background-color: $u-action-sheet-cancel-text-hover-background-color;
}
}
</style>
import defprops from '../../libs/config/props';
export default {
props: {
// 图片地址,Array<String>|Array<Object>形式
urls: {
type: Array,
default: defprops.album.urls
},
// 指定从数组的对象元素中读取哪个属性作为图片地址
keyName: {
type: String,
default: defprops.album.keyName
},
// 单图时,图片长边的长度
singleSize: {
type: [String, Number],
default: defprops.album.singleSize
},
// 多图时,图片边长
multipleSize: {
type: [String, Number],
default: defprops.album.multipleSize
},
// 多图时,图片水平和垂直之间的间隔
space: {
type: [String, Number],
default: defprops.album.space
},
// 单图时,图片缩放裁剪的模式
singleMode: {
type: String,
default: defprops.album.singleMode
},
// 多图时,图片缩放裁剪的模式
multipleMode: {
type: String,
default: defprops.album.multipleMode
},
// 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
maxCount: {
type: [String, Number],
default: defprops.album.maxCount
},
// 是否可以预览图片
previewFullImage: {
type: Boolean,
default: defprops.album.previewFullImage
},
// 每行展示图片数量,如设置,singleSize和multipleSize将会无效
rowCount: {
type: [String, Number],
default: defprops.album.rowCount
},
// 超出maxCount时是否显示查看更多的提示
showMore: {
type: Boolean,
default: defprops.album.showMore
}
}
}
This diff is collapsed.
import defprops from '../../libs/config/props';
export default {
props: {
// 显示文字
title: {
type: String,
default: defprops.alert.title
},
// 主题,success/warning/info/error
type: {
type: String,
default: defprops.alert.type
},
// 辅助性文字
description: {
type: String,
default: defprops.alert.description
},
// 是否可关闭
closable: {
type: Boolean,
default: defprops.alert.closable
},
// 是否显示图标
showIcon: {
type: Boolean,
default: defprops.alert.showIcon
},
// 浅或深色调,light-浅色,dark-深色
effect: {
type: String,
default: defprops.alert.effect
},
// 文字是否居中
center: {
type: Boolean,
default: defprops.alert.center
},
// 字体大小
fontSize: {
type: [String, Number],
default: defprops.alert.fontSize
}
}
}
This diff is collapsed.
import defprops from '../../libs/config/props';
export default {
props: {
// 头像图片组
urls: {
type: Array,
default: defprops.avatarGroup.urls
},
// 最多展示的头像数量
maxCount: {
type: [String, Number],
default: defprops.avatarGroup.maxCount
},
// 头像形状
shape: {
type: String,
default: defprops.avatarGroup.shape
},
// 图片裁剪模式
mode: {
type: String,
default: defprops.avatarGroup.mode
},
// 超出maxCount时是否显示查看更多的提示
showMore: {
type: Boolean,
default: defprops.avatarGroup.showMore
},
// 头像大小
size: {
type: [String, Number],
default: defprops.avatarGroup.size
},
// 指定从数组的对象元素中读取哪个属性作为图片地址
keyName: {
type: String,
default: defprops.avatarGroup.keyName
},
// 头像之间的遮挡比例
gap: {
type: [String, Number],
validator(value) {
return value >= 0 && value <= 1
},
default: defprops.avatarGroup.gap
},
// 需额外显示的值
extraValue: {
type: [Number, String],
default: defprops.avatarGroup.extraValue
}
}
}
import defprops from '../../libs/config/props';
export default {
props: {
// 头像图片路径(不能为相对路径)
src: {
type: String,
default: defprops.avatar.src
},
// 头像形状,circle-圆形,square-方形
shape: {
type: String,
default: defprops.avatar.shape
},
// 头像尺寸
size: {
type: [String, Number],
default: defprops.avatar.size
},
// 裁剪模式
mode: {
type: String,
default: defprops.avatar.mode
},
// 显示的文字
text: {
type: String,
default: defprops.avatar.text
},
// 背景色
bgColor: {
type: String,
default: defprops.avatar.bgColor
},
// 文字颜色
color: {
type: String,
default: defprops.avatar.color
},
// 文字大小
fontSize: {
type: [String, Number],
default: defprops.avatar.fontSize
},
// 显示的图标
icon: {
type: String,
default: defprops.avatar.icon
},
// 显示小程序头像,只对百度,微信,QQ小程序有效
mpAvatar: {
type: Boolean,
default: defprops.avatar.mpAvatar
},
// 是否使用随机背景色
randomBgColor: {
type: Boolean,
default: defprops.avatar.randomBgColor
},
// 加载失败的默认头像(组件有内置默认图片)
defaultUrl: {
type: String,
default: defprops.avatar.defaultUrl
},
// 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
colorIndex: {
type: [String, Number],
// 校验参数规则,索引在0-19之间
validator(n) {
return uni.$u.test.range(n, [0, 19]) || n === ''
},
default: defprops.avatar.colorIndex
},
// 组件标识符
name: {
type: String,
default: defprops.avatar.name
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment