qsh 1 month ago
commit edf7d20e6b
  1. 12
      .env.development
  2. 14
      .env.production
  3. 23
      .gitignore
  4. 3
      README.md
  5. 20
      index.html
  6. 9
      jsconfig.json
  7. 37
      package.json
  8. 9258
      pnpm-lock.yaml
  9. 28
      project.config.json
  10. 7
      project.private.config.json
  11. 54
      src/App.vue
  12. 596
      src/components/chose-city/chose-city.vue
  13. 43
      src/jtools/api/exam.js
  14. 96
      src/jtools/api/index.js
  15. 24
      src/jtools/api/login.js
  16. 93
      src/jtools/api/question.js
  17. 51
      src/jtools/api/station.js
  18. 45
      src/jtools/api/user.js
  19. 11
      src/jtools/constants/common.js
  20. 5
      src/jtools/constants/index.js
  21. 160
      src/jtools/pay/index.js
  22. 75
      src/jtools/platform/index.js
  23. 60
      src/jtools/request/index.js
  24. 40
      src/jtools/storage/index.js
  25. 18
      src/jtools/store/index.js
  26. 103
      src/jtools/store/user.js
  27. 45
      src/jtools/utils/index.js
  28. 31
      src/jtools/utils/throttle.js
  29. 167
      src/jtools/utils/validate.js
  30. 171
      src/jtools/wechat/sdk.js
  31. 261
      src/jtools/wechat/wechat.js
  32. 21
      src/main.js
  33. 80
      src/manifest.json
  34. 18
      src/package.json
  35. 98
      src/pages.json
  36. 33
      src/pages/choseCity/choseCity.vue
  37. 72
      src/pages/course/chapter.vue
  38. 54
      src/pages/course/detail.vue
  39. 80
      src/pages/course/index.vue
  40. 151
      src/pages/index/index.vue
  41. 13
      src/pages/me/good.vue
  42. 100
      src/pages/me/index.vue
  43. 13
      src/pages/me/like.vue
  44. 49
      src/pages/me/login.vue
  45. 15
      src/pages/me/look.vue
  46. 115
      src/pages/me/userInfo.vue
  47. 13
      src/pages/public/404.vue
  48. 27
      src/readme.md
  49. 1734
      src/static/dataJson/city.json
  50. 47
      src/static/font/iconfont.css
  51. 1
      src/static/font/iconfont.js
  52. 65
      src/static/font/iconfont.json
  53. BIN
      src/static/font/iconfont.ttf
  54. BIN
      src/static/font/iconfont.woff
  55. BIN
      src/static/font/iconfont.woff2
  56. BIN
      src/static/image/mine/default_avatar.png
  57. BIN
      src/static/image/tabbar/tab-book-selected.png
  58. BIN
      src/static/image/tabbar/tab-book.png
  59. BIN
      src/static/image/tabbar/tab-mine-selected.png
  60. BIN
      src/static/image/tabbar/tab-mine.png
  61. 3
      src/static/index.scss
  62. 434
      src/static/style/app.scss
  63. 674
      src/static/style/base.scss
  64. 3924
      src/static/style/colorui.css
  65. 129
      src/static/style/index.scss
  66. 26
      src/uni.css
  67. 1
      src/uni.min.css
  68. 76
      src/uni.scss
  69. 21
      src/uni_modules/uview-plus/LICENSE
  70. 74
      src/uni_modules/uview-plus/README.md
  71. 909
      src/uni_modules/uview-plus/changelog.md
  72. 85
      src/uni_modules/uview-plus/components/u--form/u--form.vue
  73. 50
      src/uni_modules/uview-plus/components/u--image/u--image.vue
  74. 74
      src/uni_modules/uview-plus/components/u--input/u--input.vue
  75. 45
      src/uni_modules/uview-plus/components/u--text/u--text.vue
  76. 47
      src/uni_modules/uview-plus/components/u--textarea/u--textarea.vue
  77. 109
      src/uni_modules/uview-plus/components/u-action-sheet-data/u-action-sheet-data.vue
  78. 26
      src/uni_modules/uview-plus/components/u-action-sheet/actionSheet.js
  79. 62
      src/uni_modules/uview-plus/components/u-action-sheet/props.js
  80. 282
      src/uni_modules/uview-plus/components/u-action-sheet/u-action-sheet.vue
  81. 28
      src/uni_modules/uview-plus/components/u-album/album.js
  82. 86
      src/uni_modules/uview-plus/components/u-album/props.js
  83. 300
      src/uni_modules/uview-plus/components/u-album/u-album.vue
  84. 22
      src/uni_modules/uview-plus/components/u-alert/alert.js
  85. 46
      src/uni_modules/uview-plus/components/u-alert/props.js
  86. 250
      src/uni_modules/uview-plus/components/u-alert/u-alert.vue
  87. 23
      src/uni_modules/uview-plus/components/u-avatar-group/avatarGroup.js
  88. 54
      src/uni_modules/uview-plus/components/u-avatar-group/props.js
  89. 109
      src/uni_modules/uview-plus/components/u-avatar-group/u-avatar-group.vue
  90. 28
      src/uni_modules/uview-plus/components/u-avatar/avatar.js
  91. 81
      src/uni_modules/uview-plus/components/u-avatar/props.js
  92. 179
      src/uni_modules/uview-plus/components/u-avatar/u-avatar.vue
  93. 27
      src/uni_modules/uview-plus/components/u-back-top/backtop.js
  94. 56
      src/uni_modules/uview-plus/components/u-back-top/props.js
  95. 132
      src/uni_modules/uview-plus/components/u-back-top/u-back-top.vue
  96. 27
      src/uni_modules/uview-plus/components/u-badge/badge.js
  97. 79
      src/uni_modules/uview-plus/components/u-badge/props.js
  98. 176
      src/uni_modules/uview-plus/components/u-badge/u-badge.vue
  99. 27
      src/uni_modules/uview-plus/components/u-box/props.js
  100. 91
      src/uni_modules/uview-plus/components/u-box/u-box.vue
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,12 @@
# 页面标题
VITE_APP_TITLE = 寻驾-无人机
# 开发环境配置
VITE_APP_ENV = 'development'
# 寻驾-无人机/开发环境
VITE_APP_BASE_API = 'https://cloud.ahfkbg.com/'
# VITE_APP_BASE_API = 'https://xj.ahduima.com/'
# 资源地址
VITE_UPLOAD_URL = 'https://cloud.ahfkbg.com/'

@ -0,0 +1,14 @@
# 页面标题
VITE_APP_TITLE = 寻驾-无人机
# 开发环境配置
VITE_APP_ENV = 'production'
#开发环境
VITE_APP_BASE_API = 'https://xj.ahduima.com/'
#
VITE_WEB_BASE_URL = 'https://xj.ahduima.com'
# 资源地址
VITE_UPLOAD_URL = 'http://huodong.ahduima.com'

23
.gitignore vendored

@ -0,0 +1,23 @@
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
**/*.log
tests/**/coverage/
tests/e2e/reports
selenium-debug.log
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.local
package-lock.json
yarn.lock

@ -0,0 +1,3 @@
# xj-applet
寻驾-无人机小程序

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

@ -0,0 +1,9 @@
{
"compilerOptions": {
"types": [
"@dcloudio/types",
"miniprogram-api-typings",
"mini-types"
]
}
}

@ -0,0 +1,37 @@
{
"name": "xunjia",
"version": "1.0.0",
"scripts": {
"dev:h5": "uni",
"dev": "uni -p mp-weixin",
"dev:dy": "uni -p mp-toutiao",
"build:h5": "uni build",
"build": "node updateVersion.js && uni build -p mp-weixin",
"build:dy": "uni build -p mp-toutiao",
"build-test:mp-weixin": "uni --mode test -p mp-weixin"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-alpha-3060420220922001",
"@dcloudio/uni-app-plus": "3.0.0-alpha-3060420220922001",
"@dcloudio/uni-components": "3.0.0-alpha-3060420220922001",
"@dcloudio/uni-h5": "3.0.0-alpha-3060420220922001",
"@dcloudio/uni-mp-toutiao": "3.0.0-alpha-3060420220922001",
"@dcloudio/uni-mp-weixin": "3.0.0-alpha-3060420220922001",
"dayjs": "^1.11.13",
"jsencrypt-plus": "^0.1.0",
"pinia": "2.0.36",
"pinia-plugin-persist-uni": "^1.2.0",
"vue": "^3.2.37",
"vue-i18n": "^9.1.9"
},
"devDependencies": {
"@dcloudio/types": "^3.0.13",
"@dcloudio/uni-automator": "3.0.0-alpha-3060420220922001",
"@dcloudio/uni-cli-shared": "3.0.0-alpha-3060420220922001",
"@dcloudio/uni-stacktracey": "3.0.0-alpha-3060420220922001",
"@dcloudio/vite-plugin-uni": "3.0.0-alpha-3060420220922001",
"sass": "^1.63.6",
"sass-loader": "^13.3.2",
"vite": "^2.9.14"
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,28 @@
{
"appid": "wx0668c6fabb1a9c44",
"compileType": "miniprogram",
"libVersion": "3.3.3",
"packOptions": {
"ignore": [],
"include": []
},
"setting": {
"coverView": true,
"es6": true,
"postcss": true,
"minified": true,
"enhance": true,
"showShadowRootInWxmlPanel": true,
"packNpmRelationList": [],
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
}
},
"condition": {},
"editorSetting": {
"tabIndent": "insertSpaces",
"tabSize": 2
}
}

@ -0,0 +1,7 @@
{
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"projectname": "xunjia-applet",
"setting": {
"compileHotReLoad": true
}
}

@ -0,0 +1,54 @@
<script>
import useUserStore from '@/jtools/store/user'
import { getSysConfig } from '@/jtools/api/index';
export default {
onLaunch: function (options) {
if (useUserStore().isLogin) {
useUserStore().getUserInfo()
}
getSysConfig({ configKey: 'AppletShowAll', driverTypeId: -1 }).then(res => {
let arr = []
if(res?.data?.configValue == 1) {
arr = [
{ text: '首页', icon: 'home', name: '/pages/index/index' },
{ text: '题库', icon: 'grid', name: '/pages/questionBank/index' },
{ text: '我', icon: 'account', name: '/pages/me/index' }
]
} else {
arr = [
{ text: '题库', icon: 'grid', name: '/pages/questionBank/index' },
{ text: '我', icon: 'account', name: '/pages/me/index' }
]
}
useUserStore().setTabbarList(arr)
});
},
onShow: function (options) {
// id
if (options.query?.scene) {
uni.showToast({
title: '请重新登陆绑定分销人',
icon:'none',
success() {
setTimeout(() => {
useUserStore().setSellManId(options.query.scene)
}, 1500);
}
})
}
console.log('App Show')
},
onHide: function () {
console.log('App Hide')
},
}
</script>
<style lang="scss">
@import "static/font/iconfont.css";
@import "uni_modules/uview-plus/index.scss";
@import "static/style/index.scss";
</style>

@ -0,0 +1,596 @@
<template>
<view class="chose-city">
<!-- 城市搜索 -->
<view class="city-search-wrap">
<view class="search">
<view class="l-search">
<view class="icon-search">
<view class="cuIcon-search"></view>
</view>
<input class="input-search" type="text" :value="inputValue" placeholder="请输入城市"
placeholder-style="color:#8E8F97" :focus="searchFocus" @input="searchChange" />
<text class="clear-input iconfont icon-icon-test" v-if="isClearBtn" @click="inputValue = ''"></text>
</view>
<view class="r-cancel" @click="closeModal">取消</view>
</view>
<!-- 搜索列表 -->
<view class="reach-content" v-show="inputValue">
<block v-show="searchData.length">
<view class="li" v-for="item in searchData" :key="item.citycode" @click="selectCity(item)">
{{item.name}}
</view>
</block>
<view class="has-no-data" v-show="hasNoData">没有找到匹配数据~</view>
</view>
</view>
<!-- 城市列表 -->
<scroll-view class="scroll-view" scroll-y scroll-with-animation="true" enable-back-to-top="true"
:scroll-into-view="toIndex" @scroll="scrollHandle" v-if="!inputValue">
<view class="block">
<!-- 您所在的地区 -->
<view class="area list-item" id="area">
<view class="title-wrapp">
<view class="c-title">
<text class="l">您所在的地区</text>
</view>
</view>
<view class="ul">
<view class="li now font-clamp" @click="selectCity(myCityObj,'refresh')">
<text class="text">{{ hasLocation ? myCityObj.name:'定位失败' }}</text>
</view>
</view>
</view>
<!-- 历史记录 -->
<view class="area list-item" id="record" v-if="recordList.length">
<view class="title-wrapp">
<view class="c-title">
<text class="l">历史记录</text>
</view>
</view>
<view class="ul">
<view class="li font-clamp" v-for="item in recordList" :key="item.citycode" @click="selectCity(item)">
{{ item.name }}
</view>
</view>
</view>
</view>
<!-- 城市列表 -->
<view class="city-list">
<view class="list list-item" v-for="(item, key) of cityList" :key="key" :id="item.nameType">
<view class="c-title">{{ item.nameType }}</view>
<view class="item" v-for="innerItem in item.list" :key="innerItem.citycode" @click="selectCity(innerItem)">
{{ innerItem.name }}
</view>
</view>
</view>
</scroll-view>
<!-- 字母列表 -->
<view class="alphabet" @touchstart="touchStart" @touchend="touchEnd" @touchmove.stop="touchMove">
<view v-for="(item, index) in alphabet" :key="index" @touchstart="getLetter" @touchend="setLetter" :id="item">
<view class="item" :class="{ active: currentLetter == item }">
{{ item == 'area' ? '当前' : item == 'record' ? '历史' : item }}
</view>
</view>
</view>
</view>
</template>
<script>
import cityJson from '@/static/dataJson/city.json'
export default {
data() {
return {
isIPX: null,
regionId: null, // ID
isToggle: true,
isReach: false,
inputValue: '',
searchData: [], //
isClearBtn: false,
toIndex: '', //
tipsLetter: '', //
timer: null,
hasNoData: false,
searchFocus: false,
letterDetails: [],
currentLetter: 'area', //
cityArr: [],
recordList: [],
cityList: [],
hasLocation: false,
myCityObj: {},
alphabet: [],
qqmap: null
};
},
mounted() {
this.cityArr = cityJson.data.list
if (this.cityArr && this.cityArr[0]) {
this.cityArr.map(v => {
v.nameType = v.pinyin.substr(0, 1)
})
this.cityList = this.groupArr(this.cityArr, 'nameType')
}
this.recordList = cityJson.data.recordList
this.alphabet = cityJson.data.alphabet
this.getLocation()
},
watch: {
//
inputValue(newVal) {
this.isClearBtn = newVal ? true : false;
if (this.timer) {
clearTimeout(this.timer);
}
if (!this.inputValue) {
this.searchData = [];
return;
}
this.timer = setTimeout(() => {
const result = [];
this.cityList.map(v => {
v.list.forEach((item) => {
if (/^[a-zA-Z]+$/.test(item.pinyin) && item.pinyin.toLowerCase().includes(this.inputValue
.toLowerCase()) ||
item.name.includes(this.inputValue)) {
result.push(item);
}
});
})
this.searchData = result;
if (this.searchData.length === 0) {
this.hasNoData = true;
} else {
this.hasNoData = false;
}
}, 500);
},
isReach(val) {
this.searchFocus = val;
},
},
methods: {
getLocation() {
const that = this
uni.getLocation({
// #ifdef MP-ALIPAY
type: 2,
// #endif
success(res) {
const latitude = res.latitude;
const longitude = res.longitude;
uni.request({
url: `https://apis.map.qq.com/ws/geocoder/v1/?location=${latitude},${longitude}&key=3ZYBZ-K3SK2-TGXUJ-CSTFH-7FQQ5-REFVK`,
success: function(res) {
const city = res.data.result.address_component.city;
that.myCityObj = {
name: city
};
that.hasLocation = true
}
});
},
fail(err) {
that.hasLocation = false
uni.showToast({
icon: 'none',
title: '获取用户定位失败,请手动选择当前城市'
})
},
})
},
groupArr(list, field) {
var fieldList = [],
att = [];
list.map((e) => {
fieldList.push(e[field])
})
//
fieldList = fieldList.filter((e, i, self) => {
return self.indexOf(e) == i
})
for (var j = 0; j < fieldList.length; j++) {
//
var arr = list.filter((e) => {
return e[field] == fieldList[j];
})
att.push({
nameType: arr[0].nameType,
list: arr
})
}
return att;
},
selectCity(item, type) {
if (type === 'refresh' && !this.hasLocation) {
//
return this.getLocation()
}
// console.log('', item);
this.$emit('selectCity', item)
//
this.toIndex = 'area';
setTimeout(() => {
this.toIndex = '';
}, 1000);
},
closeModal() {
this.$emit('closeModal')
},
//
scrollHandle(e) {
let view = uni.createSelectorQuery().in(this).selectAll('.list-item');
view
.boundingClientRect((d) => {
let top = d[0].top;
d.forEach((item) => {
item.top = item.top - top;
item.bottom = item.bottom - top;
this.letterDetails.push({
id: item.id,
top: item.top,
bottom: item.bottom,
});
});
})
.exec();
const scrollTop = e.detail.scrollTop;
this.letterDetails.some((item) => {
if (scrollTop >= item.top && scrollTop <= item.bottom - 20) {
this.currentLetter = item.id;
//
return true;
}
});
},
//
searchChange(e) {
let {
value
} = e.detail;
this.inputValue = value;
},
//
touchStart(e) {
// console.log(e);
},
//
touchMove(e) {
uni.vibrateShort();
let y = e.touches[0].clientY;
let offsettop = e.currentTarget.offsetTop;
//,
if (y > offsettop) {
let num = parseInt((y - offsettop) / 15); //
let letter = this.alphabet[num];
this.tipsLetter = letter;
let curentLetter = this.letterTransform(letter);
uni.showToast({
title: curentLetter,
icon: 'none',
});
}
},
//
touchEnd() {
this.toIndex = this.tipsLetter;
},
//
getLetter(e) {
uni.vibrateShort();
let {
id
} = e.currentTarget;
this.tipsLetter = id;
let curentLetter = this.letterTransform(id);
uni.showToast({
title: curentLetter,
icon: 'none',
});
},
//toIndex
setLetter() {
this.toIndex = this.tipsLetter;
},
//
letterTransform(letter) {
let str = '';
if (letter == 'area') {
str = '当前';
} else if (letter == 'record') {
str = '历史';
} else {
str = letter;
}
return str;
},
},
}
</script>
<style lang="scss" scoped>
.chose-city {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
background: #fff;
}
.city-search-wrap {
width: 100%;
box-sizing: border-box;
.search {
width: 750rpx;
height: 110rpx;
display: flex;
align-items: center;
font-size: 28rpx;
color: #222;
padding: 14rpx 36rpx;
box-sizing: border-box;
background: #fff;
.l-search {
width: 597rpx;
position: relative;
height: 72rpx;
line-height: 72rpx;
.icon-search {
font-size: 28rpx;
position: absolute;
left: 30rpx;
top: 0;
color: #8e8f97;
font-weight: 700;
height: 72rpx;
line-height: 72rpx;
}
.input-search {
width: 597rpx;
height: 72rpx;
box-sizing: border-box;
padding: 0 84rpx 0 84rpx;
text-align: left;
background: #f4f5f9;
border-radius: 12rpx;
border: 0;
}
.clear-input {
font-size: 30rpx;
position: absolute;
right: 10rpx;
top: 50%;
transform: translateY(-50%);
padding: 10rpx;
color: #8e8f97;
}
}
.r-cancel {
width: 80rpx;
box-sizing: border-box;
padding-left: 24rpx;
font-size: 28rpx;
height: 72rpx;
line-height: 72rpx;
background: transparent;
border: 0;
color: #519AD2;
}
}
}
.reach-content {
padding-left: 36rpx;
box-sizing: border-box;
.li {
width: 714rpx;
font-size: 28rpx;
height: 100rpx;
line-height: 100rpx;
color: #333;
position: relative;
box-sizing: border-box;
border-bottom: 2rpx solid #F5F5F5;
}
}
.block {
padding: 0 36rpx;
box-sizing: border-box;
}
.top-search {
line-height: 72rpx;
padding: 14rpx 30rpx 0;
box-sizing: border-box;
margin-bottom: 26rpx;
.item {
background: #F5F5F5;
border-radius: 12rpx;
font-size: 28rpx;
text-align: center;
color: #999999;
/* #ifdef MP-ALIPAY */
height: 72rpx;
line-height: 72rpx;
/* #endif */
text {
padding-left: 20rpx;
color: #c1c2cd;
vertical-align: middle;
position: relative;
top: -4rpx;
}
}
}
.scroll-view {
width: 100%;
height: calc(100vh - 110rpx);
box-sizing: border-box;
}
.area {
margin-bottom: 8rpx;
.title-wrapp {
position: sticky;
top: 0;
left: 0;
background: #fff;
}
.c-title {
width: 100%;
box-sizing: border-box;
font-size: 28rpx;
color: #999999;
margin-bottom: 24rpx;
display: inline-flex;
justify-content: space-between;
align-items: center;
.r {
font-size: 24rpx;
color: #8e8f97;
display: inline-block;
align-items: center;
.iconfont {
font-size: 24rpx;
}
}
}
.ul {
display: flex;
flex-wrap: wrap;
.li {
width: 155rpx;
padding: 0 10rpx;
box-sizing: border-box;
height: 72rpx;
line-height: 68rpx;
text-align: center;
font-size: 32rpx;
color: #333;
border-radius: 8rpx;
margin: 0 18rpx 28rpx 0;
border: 2rpx solid #E2E2E2;
&:nth-child(4n) {
margin-right: 0;
}
&.now {
width: auto;
padding: 0 32rpx 0 22rpx;
.icon {
width: 50rpx;
height: 50rpx;
background-size: 100%;
vertical-align: middle;
position: relative;
top: -4rpx;
}
.text {
padding-left: 10rpx;
}
}
&.active {
font-weight: 500;
background: #ffde45;
}
}
.hover {
background: #ffde45;
}
}
}
.city-list {
width: 750rpx;
padding-bottom: 50rpx;
.c-title {
height: 60rpx;
line-height: 60rpx;
font-size: 30rpx;
font-weight: 500;
color: #272636;
background: #fff;
box-sizing: border-box;
padding-left: 36rpx;
position: sticky;
top: 0;
left: 0;
z-index: 2;
}
.item {
width: 714rpx;
margin-left: 36rpx;
padding: 0 36rpx 0 0;
height: 100rpx;
line-height: 100rpx;
color: #333;
font-size: 28rpx;
box-sizing: border-box;
border-bottom: 2rpx solid #F5F5F5;
}
}
.alphabet {
position: fixed;
right: 0;
bottom: 20%;
width: calc(750rpx - 680rpx);
text-align: center;
font-size: 20rpx;
font-weight: 700;
color: #8e8f97;
z-index: 99;
.item {
height: 15px;
line-height: 15px;
}
.active {
color: #222;
}
}
.has-no-data {
font-size: 24rpx;
text-align: center;
color: #8e8f97;
margin-top: 50rpx;
}
</style>

@ -0,0 +1,43 @@
import request from '../request/index.js';
export function getExamQuestion(data) {
return request({
url: 'wrj-api/wrjQuestionTest/test',
method: 'GET',
data
});
}
export function getCurrentTest(data) {
return request({
url: 'wrj-api/wrjQuestionTest/getCurrentTest',
method: 'GET',
data
});
}
export function getExamInfo(data) {
return request({
url: 'wrj-api/wrjQuestionTest/getQuestionTestMessageById',
method: 'GET',
data
});
}
// 交卷
export function submitExam(data) {
return request({
url: 'wrj-api/wrjQuestionTest/testSubmit',
method: 'POST',
data
});
}
// 获取考试记录
export function getExamRecord(data) {
return request({
url: 'wrj-api/wrjQuestionTest/getTestScore',
method: 'POST',
data
});
}

@ -0,0 +1,96 @@
import request from '../request/index.js';
export function getCarTypeList(data) {
return request({
url: 'wrj-api/wrjModel/list',
method: 'GET',
data,
noToken: true
});
}
export function getDriverTypeList(data) {
return request({
url: 'wrj-api/wrjDriverType/list',
method: 'GET',
data,
noToken: true
});
}
// 获取章节列表
export function getChapterOptions(data) {
return request({
url: 'wrj-api/wrjChapter/queryChapter',
method: 'POST',
data
});
}
export function getChapterInfo(data) {
return request({
url: 'wrj-api/wrjChapter/queryChapterInfo',
method: 'POST',
data
});
}
export function getChapterDoneList(data) {
return request({
url: 'wrj-api/wrjChapter/queryChapterQuestionNumWithUser',
method: 'GET',
data
});
}
export function uploadFile(data) {
return request({
url: 'wrj-api/system/file/upload',
method: 'POST',
data
});
}
// 查询考点列表
export function queryExamPoint(data) {
return request({
url: 'wrj-api/wrjExamPlace/pageList',
method: 'POST',
data
});
}
// 查询考点时间
export function queryExamPointTime(data) {
return request({
url: 'wrj-api/wrjExamPlace/queryDate',
method: 'POST',
data
});
}
// 根据时间查询考点
export function queryExamPointByTime(data) {
return request({
url: 'wrj-api/wrjExamPlace/query',
method: 'POST',
data
});
}
// 收藏考点
export function collectExamPoint(placeId) {
return request({
url: 'wrj-api/wrjUserCollectPlace/update?placeId=' + placeId,
method: 'POST'
});
}
// 获取配置项
export function getSysConfig(data) {
return request({
url: 'wrj-api/wrjSysConfig/queryConfigByKey',
method: 'GET',
data,
noToken: true
});
}

@ -0,0 +1,24 @@
import request from '../request/index.js';
export function login(data) {
return request({
url: 'wrj-api/sysUser/login',
method: 'POST',
data,
noToken: true
});
}
export function logout() {
return request({
url: 'wrj-api/sysUser/loginOut',
method: 'get'
});
}
export function getInfo() {
return request({
url: 'wrj-api/sysUser/queryUserMessage',
method: 'get'
});
}

@ -0,0 +1,93 @@
import request from '../request/index.js';
export function queryQuestion(data) {
return request({
url: 'wrj-api/wrjQuestion/queryQuestionWithUser',
method: 'POST',
data
});
}
export function queryQuestionEmpty(data) {
return request({
url: 'wrj-api/wrjQuestion/queryQuestion',
method: 'POST',
data
});
}
export function doExercise(data) {
return request({
url: 'wrj-api/wrjQuestionPractice/insert',
method: 'POST',
data
});
}
export function clearPractice(params) {
return request({
url: `wrj-api/wrjQuestionPractice/clear?practiceType=${params.practiceType}&businessCode=${params.businessCode}`,
method: 'POST'
});
}
// 收藏
export function collectQuestion(questionId) {
return request({
url: `wrj-api/wrjUserCollectQuestion/update?questionId=${questionId}`,
method: 'POST'
});
}
// 获取收藏题目
export function queryLikeQuestion(data) {
return request({
url: `wrj-api/wrjUserCollectQuestion/getUserCollectQuestion`,
method: 'GET',
data
});
}
// 获取错题
export function queryErrorQuestion(data) {
return request({
url: `wrj-api/wrjUserErrorQuestion/getUserErrorQuestion`,
method: 'GET',
data
});
}
// 获取收藏的列表
export function queryCollectList(driverTypeId) {
return request({
url: `wrj-api/wrjUserCollectQuestion/getUserCollectQuestionId?driverTypeId=${driverTypeId}`,
method: 'GET'
});
}
// 移除错题
export function removeErrorQuestion(questionId) {
return request({
url: `wrj-api/wrjUserErrorQuestion/remove?questionId=${questionId}`,
method: 'POST'
});
}
// 获取首页信息
export function queryIndexInfo(data) {
return request({
url: 'wrj-api/wrjQuestion/getIndexData',
method: 'GET',
data,
publicApi: true
});
}
// 获取考试信息
export function queryIndexDetail(data) {
return request({
url: 'wrj-api/wrjDriverType/detail',
method: 'GET',
data
});
}

@ -0,0 +1,51 @@
import request from '../request/index.js';
export function getStationList(data) {
return request({
url: 'wrj-api/wrjSchool/pageList',
method: 'POST',
data,
noToken: true
});
}
// 培训机构注册
export function registerStation(data) {
return request({
url: 'wrj-api/wrjSchool/insert',
method: 'POST',
data,
noToken: true
});
}
export function getStationDetail(schoolId) {
return request({
url: 'wrj-api/wrjSchool/detail?schoolId=' + schoolId,
method: 'GET',
publicApi: true
});
}
// 收藏
export function collectStation(schoolId) {
return request({
url: 'wrj-api/wrjUserCollectSchool/update?schoolId=' + schoolId,
method: 'POST',
});
}
// 获取驾培机型
export function getCarTypeList() {
return request({
url: 'wrj-api/wrjSchoolDriverType/list',
method: 'GET',
});
}
// 查询收藏的机构
export function queryCollectList() {
return request({
url: 'wrj-api/wrjUserCollectSchool/myList',
method: 'POST',
});
}

@ -0,0 +1,45 @@
import request from '../request/index.js';
export function chooseType(data) {
return request({
url: 'wrj-api/sysUser/driverTypeChoose',
method: 'POST',
data
});
}
// 修改用户资料
export function updateUserInfo(data) {
return request({
url: 'wrj-api/sysUser/update',
method: 'POST',
data
});
}
// 教员入驻
export function teacherRegister(data) {
return request({
url: 'wrj-api/wrjTeacher/apply',
method: 'POST',
data
});
}
// 报名咨询
export function applyConsult(data) {
return request({
url: 'wrj-api/wrjClue/consult',
method: 'POST',
data
});
}
// 获取收藏的考点
export function getCollecRoomList() {
return request({
url: 'wrj-api/wrjUserCollectPlace/myList',
method: 'POST',
});
}

@ -0,0 +1,11 @@
// 请求状态码
const RESPONSE_TYPE = {
SUCCESS: '0000',
RELOGIN: 'E403',
ERROR: '4000'
};
export default {
RESPONSE_TYPE,
};

@ -0,0 +1,5 @@
import common from './common.js'
export default {
...common
}

@ -0,0 +1,160 @@
import request from '../request/index.js';
// #ifdef H5
// import wxsdk from '@/jtools/wechat/sdk'
// #endif
// import wechat from '@/jtools/wechat/wechat'
import $platform from '@/jtools/platform';
import {prePay} from '@/jtools/api/pay'
/**
* 支付
*
* @param {String} payment = ['wechat','alipay','wallet'] - 支付方式
* @param {Object} order = {} - 订单详情
* @param {String} orderType = ['goods','recharge'] - 订单类型
*/
export default class JtoolsPay {
// wxOfficialAccount wxMiniProgram App H5
// wechat 公众号JSSDK支付 小程序支付 微信开放平台支付 H5网页支付
// alipay 复制网址 复制网址 支付宝开放平台支付 直接跳转链接
// wallet v v v v
constructor(payment, order, orderType) {
this.payment = payment;
this.order = order;
this.orderType = orderType;
this.platform = $platform.get();
let payMehod = this.getPayMethod();
payMehod();
}
getPayMethod() {
var payMethod = {
'wxMiniProgram': {
'wechat': () => {
this.wxMiniProgramPay()
},
},
'App': {
'wechat': () => {
this.wechatPay()
},
'alipay': () => {
this.aliPay()
},
},
}
return payMethod[this.platform][this.payment];
}
// 预支付
prepay() {
let that = this;
return new Promise((resolve, reject) => {
const p = $platform.device()
const tradeInfoType = p == 'android' ? 'Android' : p == 'ios' ? 'iOS' : 'Wap'
let params = {
"code":this.order.code,
"description": this.order.description,
"money": this.order.money,
"outTradeNo": this.order.outTradeNo,
"userId": this.order.userId,
"tradeType":'1',
"detailId": this.order.detailId
}
if (uni.getStorageSync('openId')) {
params.openId = uni.getStorageSync('openId');
}
prePay(params).then(res => {
console.log('预支付',res);
if (res.code == '0000') {
resolve(res);
}
})
});
}
// 微信小程序支付
async wxMiniProgramPay() {
let that = this;
let result = await this.prepay();
const params = result.data
uni.requestPayment({
provider: 'wxpay',
...{
appId: params.appId, //公众号名称,由商户传入
timeStamp: params.timeStamp, //时间戳,自1970年以来的秒数
nonceStr: params.nonceStr, //随机串
package: params.packageVal,
signType: params.signType, //微信签名方式:
paySign: params.paySign, //微信签名
},
success: res => {
console.log(res);
that.payResult('success', result.data.orderPayNo)
},
fail: err => {
console.log('支付取消或者失败:', err);
err.errMsg !== "requestPayment:fail cancel" && that.payResult('fail')
}
});
}
// 支付宝支付
async aliPay() {
let that = this;
let result = await this.prepay();
if (result.code === 1) {
uni.requestPayment({
provider: 'alipay',
orderInfo: result.data.pay_data, //支付宝订单数据
success: res => {
that.payResult('success')
},
fail: err => {
err.errMsg !== "requestPayment:fail cancel" && that.payResult('fail')
}
});
}
}
// 微信支付
async wechatPay() {
let that = this;
let result = await this.prepay();
console.log('微信支付');
if (result.code === 1) {
uni.requestPayment({
provider: 'wxpay',
orderInfo: JSON.parse(result.data.pay_data), //微信订单数据(官方说是string。实测为object)
success: res => {
that.payResult('success')
},
fail: err => {
err.errMsg !== "requestPayment:fail cancel" && that.payResult('fail')
console.log('支付取消或者失败:', err);
}
});
}
}
// 支付结果跳转,success:成功,fail:失败
payResult(resultType, orderPayNo) {
const that = this;
let path = 'paySuccess'
uni.navigateTo({
url: path
})
}
}

@ -0,0 +1,75 @@
/**
* Platform v1.0.0
* @Class Platform
* @description jtools-platform 1.0.0 全平台兼容
* @Author lidongtony
* @Date 2021-04-07
* @Email lidongtony@qq.com
*/
// #ifdef H5
// 微信H5
import wxsdk from '@/jtools/wechat/sdk';
// #endif
export default {
// 获取当前运行平台
get() {
let platform = '';
// #ifdef H5
wxsdk.isWechat() ? (platform = 'wxOfficialAccount') : (platform = 'H5');
// #endif
// #ifdef APP-PLUS
platform = 'App';
// #endif
// #ifdef MP-WEIXIN
platform = 'wxMiniProgram';
// #endif
// #ifdef MP-ALIPAY
platform = 'alipayMiniProgram';
// #endif
if (platform !== '') {
uni.setStorageSync('platform', platform);
} else {
uni.showToast({
title: '暂不支持该平台',
icon: 'none'
});
}
return platform;
},
set(platform) {
uni.setStorageSync('platform', platform);
return platform;
},
// 检测当前运行机型
device() {
return uni.getDeviceInfo().platform;
},
// 获取前端真实主机
host() {
let host = location.origin;
let basePath = router.$route.options.base;
let mode = router.$route.options.mode;
host += basePath;
if (mode === 'hash') {
host += '#/';
}
return host;
},
// 处理wechat jssdk 签名网址(针对IOS微信浏览器做优化)
entry() {
let that = this;
var entryUrl = location.href;
if (this.device() === 'ios') {
if (typeof location.entryUrl !== 'undefined') {
entryUrl = location.entryUrl;
} else {
location.entryUrl = entryUrl;
}
}
return entryUrl;
}
};

@ -0,0 +1,60 @@
import storage from '../storage/index.js';
import useUserStore from '@/jtools/store/user';
//把配置项单独处理
let server_url = ' '; // 请求地址
let token = ' '; // 凭证
server_url = import.meta.env.VITE_APP_BASE_API; //环境配置
function service(options = {}) {
storage.get('token') && (token = storage.get('token'));
options.url = `${server_url}${options.url}`;
if (!options.noToken) {
if (!!options.publicApi) {
if (token.trim()) {
options.header = {
Authorization: `${token}`
};
}
} else if (!token.trim()) {
uni.navigateTo({
url: '/pages/me/login'
});
} else {
options.header = {
Authorization: `${token}`
};
}
}
return new Promise((resolved, rejected) => {
//成功
options.success = res => {
if (res.data.code == 'E403') {
// 未登录
uni.showToast({
title: res?.data?.message || '请重新登录',
icon: 'none'
});
useUserStore().logoutWithoutToken();
//请求成功
resolved(res.data);
} else if (res.data.code != '0000' && res.data.code != '4001' && res.data.code != 200) {
uni.hideLoading();
uni.showToast({
title: res?.data?.message || '访问出错',
icon: 'none'
});
resolved(res.data);
} else {
//请求成功
resolved(res.data);
}
};
//错误
options.fail = err => {
rejected(err); //错误
};
uni.request(options);
});
}
export default service;

@ -0,0 +1,40 @@
const APP_NAME = import.meta.env.VITE_APP_TITLE
export default {
set(key, value) {
// 命名规则 小程序名称-环境
const storageName = `${APP_NAME}-${process.env.NODE_ENV}`
const temp = uni.getStorageSync(storageName) || {}
temp[key] = value
uni.setStorageSync(storageName, temp)
},
get(key) {
// 命名规则 小程序名称-环境
const storageName = `${APP_NAME}-${process.env.NODE_ENV}`
const temp = uni.getStorageSync(storageName) || {}
if(temp.hasOwnProperty(key)) {
return temp[key]
} else {
return undefined
}
},
remove(key) {
// 命名规则 小程序名称-环境
const storageName = `${APP_NAME}-${process.env.NODE_ENV}`
const temp = uni.getStorageSync(storageName) || {}
if(temp.hasOwnProperty(key)) {
delete temp[key];
uni.setStorageSync(storageName, temp);
}
},
has(key) {
// 命名规则 小程序名称-环境
const storageName = `${APP_NAME}-${process.env.NODE_ENV}`
const temp = uni.getStorageSync(storageName) || {}
return temp.hasOwnProperty(key)
},
clear() {
// 命名规则 小程序名称-环境
const storageName = `${APP_NAME}-${process.env.NODE_ENV}`
uni.removeStorageSync(storageName)
}
}

@ -0,0 +1,18 @@
import { createPinia } from 'pinia';
// 自动注入所有pinia模块
const files = import.meta.globEager('./*.js');
const modules = {};
Object.keys(files).forEach((key) => {
modules[key.replace(/(.*\/)*([^.]+).*/gi, '$2')] = files[key].default;
});
export const setupPinia = (app) => {
const pinia = createPinia();
app.use(pinia);
};
export default (name) => {
return modules[name]();
};

@ -0,0 +1,103 @@
import { defineStore } from 'pinia';
import { login, getInfo } from '@/jtools/api/login';
import storage from '@/jtools/storage';
const useUserStore = defineStore({
id: 'user',
state: () => ({
token: storage.get('token'),
isLogin: storage.get('isLogin'), // 是否登陆
userInfo: {}, // 用户信息
sellManId: '', // 分销人id
currentType: {
modelId: '10001',
typeId: '20021',
showName: '多旋翼 - 视距内驾驶员'
},
tabbarList: []
}),
actions: {
login(params) {
return new Promise(async (resolve, reject) => {
let newParams = { ...params };
if (this.sellManId) {
newParams = { ...params, distributionId: this.sellManId };
}
const resp = await login(newParams);
if (resp.code === '0000') {
// 保存登录信息,用于重新登录
this.isLogin = true;
this.token = resp.data.token;
this.userInfo = resp.data;
storage.set('isLogin', true);
storage.set('token', resp.data.token);
storage.set('userInfo', resp.data);
resolve(resp.data);
} else {
reject();
}
});
},
// 登出
logout(force = false) {
return new Promise((resolve, reject) => {
this.resetUserData();
uni.navigateTo({
url: '/pages/me/login'
});
resolve();
});
},
//过期登出
logoutWithoutToken(force = false) {
return new Promise((resolve, reject) => {
this.resetUserData();
resolve();
});
},
setSellManId(id) {
this.sellManId = id;
this.logout();
},
// 获取用户信息
getUserInfo() {
getInfo().then(resp => {
if (resp.code == '0000') {
this.userInfo = resp.data;
if (resp.data.driverTypeId && resp.data.modelId) {
this.currentType = {
modelId: resp.data.modelId,
typeId: resp.data.driverTypeId,
showName: `${resp.data.modelName} - ${resp.data.driverTypeName}`
};
}
storage.set('userInfo', resp.data);
}
});
},
setUserInfo(info) {
this.userInfo = info;
},
resetUserData() {
this.isLogin = false;
this.token = '';
this.userInfo = {};
storage.remove('isLogin');
storage.remove('token');
storage.remove('userInfo');
},
setType(modelId, typeId, showName) {
this.currentType = {
modelId,
typeId,
showName
};
},
setTabbarList(list) {
this.tabbarList = list;
}
}
});
export default useUserStore;

@ -0,0 +1,45 @@
export function numberToChinese(num, isUpper = false) {
// 检查是否为合法的正整数
if (typeof num !== 'number' || num < 0 || !Number.isInteger(num)) {
throw new Error('请输入非负整数');
}
// 中文数字字符集
const digits = isUpper ? ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'] : ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
// 中文单位
const units = isUpper ? ['', '拾', '佰', '仟', '万', '拾', '佰', '仟', '亿', '拾', '佰', '仟', '万亿'] : ['', '十', '百', '千', '万', '十', '百', '千', '亿', '十', '百', '千', '万亿'];
// 处理零
if (num === 0) return digits[0];
let chinese = '';
let numStr = num.toString();
const len = numStr.length;
for (let i = 0; i < len; i++) {
const digit = parseInt(numStr[i]);
const position = len - i - 1; // 当前数字的位置
// 处理零
if (digit === 0) {
// 如果不是最后一位且下一位不是零,添加零
if (i < len - 1 && parseInt(numStr[i + 1]) !== 0 && !chinese.endsWith(digits[0])) {
chinese += digits[0];
}
// 处理万亿、亿、万的单位
if (position % 4 === 0 && chinese && !chinese.endsWith(units[position])) {
chinese += units[position];
}
} else {
chinese += digits[digit] + units[position];
}
}
// 特殊处理十位数(如11 => 十一,而非一十)
if (len === 2 && parseInt(numStr[0]) === 1 && !isUpper) {
chinese = chinese.substring(1); // 去掉"一十"的"一"
}
return chinese;
}

@ -0,0 +1,31 @@
let timer;
let flag;
/**
* 节流原理在一定时间内只能触发一次
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
function throttle(func, wait = 500, immediate = true) {
if (immediate) {
if (!flag) {
flag = true;
// 如果是立即执行,则在wait毫秒内开始时执行
typeof func === 'function' && func();
timer = setTimeout(() => {
flag = false;
}, wait);
} else {
}
} else if (!flag) {
flag = true;
// 如果是非立即执行,则在wait毫秒内的结束处执行
timer = setTimeout(() => {
flag = false;
typeof func === 'function' && func();
}, wait);
}
}
export default throttle;

@ -0,0 +1,167 @@
/**
* 判断url是否是http或https
* @param {string} path
* @returns {Boolean}
*/
export function isHttp(url) {
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1;
}
/**
* 判断path是否为外链
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path);
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validUsername(str) {
const valid_map = ['admin', 'editor'];
return valid_map.indexOf(str.trim()) >= 0;
}
/**
* @param {string} url
* @returns {Boolean}
*/
export function isURL(url) {
const reg =
/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/;
return reg.test(url);
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validLowerCase(str) {
const reg = /^[a-z]+$/;
return reg.test(str);
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validUpperCase(str) {
const reg = /^[A-Z]+$/;
return reg.test(str);
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validAlphabets(str) {
const reg = /^[A-Za-z]+$/;
return reg.test(str);
}
/**
* @param {string} email
* @returns {Boolean}
*/
export function isEmail(email) {
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return reg.test(email);
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function isString(str) {
if (typeof str === 'string' || str instanceof String) {
return true;
}
return false;
}
export function isPhone(str) {
return str && /^1[3456789]\d{9}$/.test(str)
}
/**
* @param {Array} arg
* @returns {Boolean}
*/
export function isArray(arg) {
if (typeof Array.isArray === 'undefined') {
return Object.prototype.toString.call(arg) === '[object Array]';
}
return Array.isArray(arg);
}
// 是否纯英文
export function isAllEN(val) {
return /^[a-zA-Z]*$/.test(val);
}
// 是否纯中文
export function isAllCN(val) {
return /^[\u4E00-\u9FA5]*$/.test(val);
}
// 校验手机号
export function validPhone(rule, value, callback) {
if (value && !/^1[3456789]\d{9}$/.test(value)) {
return callback(new Error('请输入正确的11位号码'));
} else {
return callback();
}
}
// 校验固话和手机号
export function validPhoneAndMobile(rule, value, callback) {
if (value && !/^((0\d{2,3}-?\d{7,8})|(1[3465789]\d{9}))$/.test(value)) {
return callback(new Error('请输入正确的电话号码'));
} else {
return callback();
}
}
// 校验邮箱
export function validEmail(rule, value, callback) {
if (value && !isEmail(value)) {
return callback(new Error('请输入正确的邮箱'));
} else {
return callback();
}
}
// 校验纳税人识别号
export function validTaxpayer(rule, value, callback) {
if (value && !/^[A-Z0-9]{15}$|^[A-Z0-9]{18}$|^[A-Z0-9]{20}$/.test(value)) {
return callback(new Error('请输入正确的纳税人识别号'));
} else {
return callback();
}
}
// 校验是否网站
export function validUrl(rule, value, callback) {
if (value && !isURL(value)) {
return callback(new Error('请输入正确的网站'));
} else {
return callback();
}
}
// 校验银行卡
export function validBankCard(rule, value, callback) {
const strBin = '10,18,30,35,37,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,58,60,62,65,68,69,84,87,88,94,95,98,99';
if (!value) {
return callback();
} else if (!Number.isInteger(+value)) {
callback(new Error('银行卡号必须全为数字'));
} else if (value.trim().length < 8 || value.trim().length > 32) {
callback(new Error('银行卡号长度必须在8到32之间'));
} else {
callback();
}
}

@ -0,0 +1,171 @@
// var jweixin = require("jweixin-module");
import * as jweixin from 'jweixin-module';
import http from '@/jtools/request/index';
import $platform from '@/jtools/platform';
export default {
//判断是否在微信中
isWechat() {
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/micromessenger/i) == 'micromessenger') {
return true;
} else {
return false;
}
},
// 鉴权页面
initJssdk(callback) {
http('common.wxJssdk', {
uri: encodeURIComponent($platform.entry())
}).then(res => {
jweixin.config({
debug: res.data.debug,
appId: res.data.appId,
timestamp: res.data.timestamp,
nonceStr: res.data.nonceStr,
signature: res.data.signature,
jsApiList: res.data.jsApiList,
openTagList: res.data.openTagList
});
if (callback) {
callback(res.data);
}
});
},
//在需要定位页面调用
getLocation(callback) {
this.isWechat() &&
this.initJssdk(function (res) {
jweixin.ready(function () {
jweixin.getLocation({
type: 'gcj02', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
success: function (res) {
callback(res);
},
fail: function (res) {
console.log('%c微信H5sdk,getLocation失败:', 'color:green;background:yellow');
}
});
});
});
},
//获取微信收货地址
openAddress(callback) {
this.isWechat() &&
this.initJssdk(function (res) {
jweixin.ready(function () {
jweixin.openAddress({
success: function (res) {
callback(res);
},
fail: function (err) {
console.log('%c微信H5sdk,openAddress失败:', 'color:green;background:yellow');
},
complete: function (msg) {}
});
});
});
},
// 微信扫码
scanQRCode(callback) {
this.isWechat() &&
this.initJssdk(function (res) {
jweixin.ready(function () {
jweixin.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码,默认二者都有
success: function (res) {
callback(res);
},
fail: function (res) {
console.log('%c微信H5sdk,scanQRCode失败:', 'color:green;background:yellow');
}
});
});
});
},
// 微信分享
share(data, callback) {
this.isWechat() &&
this.initJssdk(function (res) {
jweixin.ready(function () {
var shareData = {
title: data.title,
desc: data.desc,
link: data.path,
imgUrl: data.image,
success: function (res) {
callback(res);
// 分享后的一些操作,比如分享统计等等
},
cancel: function (res) {}
};
jweixin.updateAppMessageShareData(shareData); //新版接口
//分享到朋友圈接口
// jweixin.updateTimelineShareData(shareData);
});
});
},
// 打开坐标位置
openLocation(data, callback) {
//打开位置
this.isWechat() &&
this.initJssdk(function (res) {
jweixin.ready(function () {
jweixin.openLocation({
//根据传入的坐标打开地图
latitude: data.latitude,
longitude: data.longitude
});
});
});
},
// 选择图片
chooseImage(callback) {
//选择图片
this.isWechat() &&
this.initJssdk(function (res) {
jweixin.ready(function () {
jweixin.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album'],
success: function (rs) {
callback(rs);
}
});
});
});
},
//微信支付
wxpay(data, callback) {
let that = this;
this.isWechat() &&
this.initJssdk(function (res) {
jweixin.ready(function () {
jweixin.chooseWXPay({
timestamp: data.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: data.nonceStr, // 支付签名随机串,不长于 32 位
package: data.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
signType: data.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
paySign: data.paySign, // 支付签名
success: function (res) {
callback(res);
},
fail: function (res) {
console.log('%c微信H5sdk,chooseWXPay失败:', 'color:green;background:yellow');
callback(res);
},
cancel: function (res) {}
});
});
});
}
};

@ -0,0 +1,261 @@
/**
* Wechat v1.1.0
* @Class Wechat
* @description jtools-wechat 1.1.0 wehcat第三方登录组件
* @Author lidongtony
* @Date 2020-05-20
* @Email lidongtony@qq.com
*/
import api from "@/jtools/request/index";
import $platform from "@/jtools/platform";
import store from "@/jtools/store";
import {
API_URL
} from "@/env";
export default {
eventMap(event) {
let map = "";
switch (event) {
case "login":
map = "登录中...";
break;
case "refresh":
map = "更新中...";
break;
case "bind":
map = "绑定中...";
break;
}
return map;
},
async login() {
let token = "";
// #ifdef MP-WEIXIN
token = await this.wxMiniProgramOauth("login");
return token;
// #endif
// #ifdef H5
this.wxOfficialAccountOauth("login");
// #endif
// #ifdef APP-PLUS
token = await this.wxOpenPlatformOauth("login");
return token;
// #endif
},
async refresh() {
let token = "";
// #ifdef MP-WEIXIN
token = await this.wxMiniProgramOauth("refresh");
return token;
// #endif
// #ifdef H5
this.wxOfficialAccountOauth("refresh");
// #endif
// #ifdef APP-PLUS
token = await this.wxOpenPlatformOauth("refresh");
return token;
// #endif
},
async bind() {
let token = "";
// #ifdef MP-WEIXIN
token = await this.wxMiniProgramOauth("bind");
return token;
// #endif
// #ifdef H5
this.wxOfficialAccountOauth("bind");
// #endif
// #ifdef APP-PLUS
token = await this.wxOpenPlatformOauth("bind");
return token;
// #endif
},
// #ifdef H5
// 微信公众号网页登录&刷新头像昵称&绑定
wxOfficialAccountOauth(event = "login") {
if ($platform.get() !== "wxOfficialAccount") {
uni.showToast({
title: "请在微信浏览器中打开",
icon: "none"
});
throw false;
}
let host = $platform.host();
let payloadObject = {
host: host,
event,
token: (event !== "login" && store.getters.isLogin) ? uni.getStorageSync("token") : ""
};
let payload = encodeURIComponent(JSON.stringify(payloadObject));
let redirect_uri = encodeURIComponent(`${API_URL}user/wxOfficialAccountOauth?payload=${payload}`);
let oauthUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + store.getters.initWechat.appid +
`&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_userinfo&state=1`;
uni.setStorageSync("lastPage", window.location.href);
window.location = oauthUrl;
},
// 微信公众号网页静默登录:临时登录获取OpenId 不入库不绑定用户
wxOfficialAccountBaseLogin() {
let state = encodeURIComponent(window.location.href);
window.location = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + store.getters.initWechat.appid +
`&redirect_uri=${API_URL}user/wxOfficialAccountBaseLogin&response_type=code&scope=snsapi_base&state=${state}`;
throw "stop";
},
// #endif
// #ifdef APP-PLUS
// 微信开放平台登录
wxOpenPlatformOauth(event = "login") {
let that = this;
return new Promise((resolve, reject) => {
uni.login({
provider: "weixin",
success: function(loginRes) {
if (loginRes.errMsg === "login:ok") {
let authResult = loginRes.authResult;
api("user.wxOpenPlatformOauth", {
authResult,
event
}, that.eventMap(event)).then(res => {
if (res.code === 1) {
resolve(res.data.token);
} else {
resolve(false);
}
});
}
},
fail: function(res) {
uni.showToast({
title: "登录失败,请稍后再试"
});
resolve(false);
api("common.debug", {
info: res
});
},
complete: function(res) {}
});
});
},
// #endif
// #ifdef MP-WEIXIN
// 微信小程序静默登录
async getWxMiniProgramSessionKey(autoLogin = true) {
let sessionStatus = false;
let session_key = "";
return new Promise((resolve, reject) => {
uni.checkSession({
success(res) {
if (res.errMsg === "checkSession:ok") sessionStatus = true;
},
complete() {
if (uni.getStorageSync("session_key") && sessionStatus && !autoLogin) {
resolve(uni.getStorageSync("session_key"));
} else {
uni.login({
success: function(info) {
let code = info.code;
api("user.getWxMiniProgramSessionKey", {
code: code,
autoLogin: autoLogin
}).then(res => {
if (res.code === 1) {
uni.setStorageSync("session_key", res
.data.session_key);
if (autoLogin) {
if (res.data.token) {
resolve(res.data.token);
} else {
resolve(false);
}
}
resolve(res.data.session_key);
} else {
reject(res.msg);
}
});
}
});
}
}
});
});
},
// 微信小程序获取用户信息登录
wxMiniProgramOauth(event = "login") {
let that = this;
let session_key = uni.getStorageSync("session_key");
uni.showLoading({
title: that.eventMap(event)
});
return new Promise((resolve, reject) => {
uni.getUserProfile({ // 必须手动确认触发
desc: "完善会员资料", // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: res => {
if (res.errMsg === "getUserProfile:ok") {
api("user.wxMiniProgramOauth", {
event,
session_key,
encryptedData: res.encryptedData,
iv: res.iv,
signature: res.signature,
}).then(res => {
console.log(res)
if (res.code === 1) {
resolve(res.data.token);
} else {
uni.removeStorageSync("session_key");
that.getWxMiniProgramSessionKey(false);
resolve(false);
}
});
}
},
complete: res => {
uni.hideLoading();
}
});
});
},
// 小程序更新
checkMiniProgramUpdate() {
if (uni.canIUse("getUpdateManager")) {
const updateManager = uni.getUpdateManager();
updateManager.onCheckForUpdate(function(res) {
// 请求完新版本信息的回调
if (res.hasUpdate) {
updateManager.onUpdateReady(function() {
uni.showModal({
title: "更新提示",
content: "新版本已经准备好,是否重启应用?",
success: function(res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate();
}
}
});
});
updateManager.onUpdateFailed(function() {
// 新的版本下载失败
uni.showModal({
title: "已经有新版本了哟~",
content: "新版本已经上线啦~,请您删除当前小程序,重新搜索打开哟~"
});
});
}
});
}
},
// #endif
};

@ -0,0 +1,21 @@
import { createSSRApp } from 'vue';
import uviewPlus from './uni_modules/uview-plus';
import platform from '@/jtools/platform';
import constants from '@/jtools/constants';
import storage from '@/jtools/storage';
import * as Pinia from 'pinia';
import App from './App.vue';
import mpShare from '@/uni_modules/uview-plus/libs/mixin/mpShare.js';
export function createApp() {
const app = createSSRApp(App);
app.config.globalProperties.$platform = platform;
app.config.globalProperties.$constants = constants;
app.config.globalProperties.$storage = storage;
app.use(uviewPlus);
app.use(Pinia.createPinia());
app.mixin(mpShare);
return {
app,
Pinia
};
}

@ -0,0 +1,80 @@
{
"name": "寻驾-无人机",
"appid": "__UNI__59447F7",
"description": "",
"versionName": "1.0.2",
"versionCode": "100",
"transformPx": false,
"app-plus": {
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
},
"modules": {},
"distribute": {
"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": {},
"sdkConfigs": {}
}
},
"quickapp": {},
"mp-weixin": {
"appid": "wx0668c6fabb1a9c44",
"setting": {
"urlCheck": true,
"minified": true,
"es6": true,
"postcss": true
},
"usingComponents": true,
"permission": {
"scope.userLocation": {
"desc": "根据城市获取不同题库"
}
}
},
"mp-alipay": {
"usingComponents": true
},
"mp-baidu": {
"usingComponents": true
},
"mp-toutiao": {
"usingComponents": true,
"appid": "ttbbd1cd6c24e1c00801",
"setting": {
"es6": true,
"postcss": true,
"minified": true
}
},
"uniStatistics": {
"enable": false
},
"vueVersion": "3",
"fallbackLocale": "zh-Hans",
"locale": "zh-Hans"
}

@ -0,0 +1,18 @@
{
"id": "yan-qr",
"name": "动态生成二维码",
"displayName": "动态生成二维码",
"version": "1.0.0",
"description": "动态生成二维码",
"keywords": [
"二维码",
"生成二维码",
"动态二维码"
],
"dcloudext": {
"category": [
"前端组件",
"通用组件"
]
}
}

@ -0,0 +1,98 @@
{
"easycom": {
"^u-(.*)": "uview-plus/components/u-$1/u-$1.vue"
},
"pages": [
{
"path": "pages/course/index",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/index/index",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/choseCity/choseCity",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/me/index",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/course/chapter",
"style": {
"navigationBarTitleText": "章节"
}
},
{
"path": "pages/course/detail",
"style": {
"navigationBarTitleText": "课程详情"
}
},
{
"path": "pages/me/userInfo",
"style": {
"navigationBarTitleText": "个人信息"
}
},
{
"path": "pages/me/good",
"style": {
"navigationBarTitleText": "我的点赞"
}
},
{
"path": "pages/me/like",
"style": {
"navigationBarTitleText": "我的收藏"
}
},
{
"path": "pages/me/look",
"style": {
"navigationBarTitleText": "我的关注"
}
},
{
"path": "pages/me/login",
"style": {
"navigationBarTitleText": "登录"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "寻驾-无人机",
"navigationBarBackgroundColor": "#FFF",
"backgroundColor": "#FFF"
},
"tabBar": {
"color": "#333",
"selectedColor": "#18AFFF",
"backgroundColor": "#FFF",
"list": [
{
"pagePath": "pages/course/index",
"text": "课程",
"iconPath": "static/image/tabbar/tab-book.png",
"selectedIconPath": "static/image/tabbar/tab-book-selected.png"
},
{
"pagePath": "pages/me/index",
"text": "我的",
"iconPath": "static/image/tabbar/tab-mine.png",
"selectedIconPath": "static/image/tabbar/tab-mine-selected.png"
}
]
}
}

@ -0,0 +1,33 @@
<template>
<view>
<chose-city @selectCity="selectCity" v-if="showCity" @closeModal="closeModal"></chose-city>
</view>
</template>
<script>
import choseCity from "@/components/chose-city/chose-city"
export default {
components: {
choseCity
},
data() {
return {
showCity:true
}
},
methods: {
selectCity(item) {
uni.setStorageSync('city', item.name)
uni.navigateBack()
// this.showCity = false
},
closeModal() {
this.showCity = false
},
}
}
</script>
<style>
</style>

@ -0,0 +1,72 @@
<template>
<view class="p15">
<view class="chapter_item" v-for="(item, index) of chapterList" :key="index" @tap="toDetail(item)">
<view style="line-height: 20px;">
<text class="j-tag" style="background: #DDEEFF;color: #1A8CFE">{{ String(index+1).padStart(2,'0') }}·庄子</text>
<text class="fs14 ml5 ">阴极阳转阳转阴转实现生命永恒的自然轮转</text>
</view>
<view class="mt10 flex ai-c jc-sb fs12 mt8 cor-999">
<text>郭老师</text>
<text>2020-01-01 19:00</text>
<view class="flex ai-c">
<u-icon name="eye" color="#999" size="14" :label="index*7" label-color="#999" label-size="12"></u-icon>
<u-icon class="ml20" name="thumb-up" color="#999" size="14" :label="index*3" label-color="#999" label-size="12"></u-icon>
</view>
</view>
</view>
</view>
</template>
<script>
// import { getChapterDoneList } from '@/jtools/api/index'
export default {
data() {
return {
chapterList:[],
}
},
onLoad(){
this.getChapterList()
},
methods:{
getChapterList(){
this.chapterList = [1,2,3]
// getChapterDoneList({driverTypeId}).then(resp=>{
// if(resp.code==='0000'){
// this.chapterList=resp.data
// }
// })
},
toDetail(chapter){
uni.navigateTo({
url: `/pages/course/detail?id=${chapter.id}`
});
},
}
}
</script>
<style lang="scss" scoped>
.chapter_item {
padding: 10px;
margin-bottom: 10px;
border-radius: 5px;
background-color: #fff;
}
.chapter_item:hover {
background-color: rgb(210, 209, 214);
}
.j-tag {
// display: inline-flex;
// align-items: center;
// justify-content: center;
padding: 1px 3px;
// height: 16px;
border-radius: 3px;
font-size: 12px;
line-height: 14px;
color: #fff;
}
</style>

File diff suppressed because one or more lines are too long

@ -0,0 +1,80 @@
<template>
<view class="wp100 hp100">
<view class="p5" style="background-color: #fff;">
<u-search v-model="queryParams.placeName" placeholder="搜索标题/作者" :showAction="false"
@search="handleSearch">搜索</u-search>
</view>
<u-list v-if="pageList.length > 0" @scrolltolower="scrolltolower" class="course-list">
<u-list-item v-for="(item, index) in pageList" :key="index">
<view class="flex ai-c jc-sb p10lr p5tb bc-fff" @click="handleClick(item)">
<view class="flex ai-c">
<image src="https://ss-cloud.ahduima.com/wrj/file/1949715991365947392.jpg" mode="aspectFill" style="width: 160px;height: 100px;border-radius: 6px;" />
</view>
<view class="flex-1 ml10 overflow-h">
<view class="fs14">课程标题 {{ index + 1 }}</view>
<view class="fs13 mt8 cor-999 text-ellipsis">课程简介内容简要描述课程内容和目标</view>
<view class="flex ai-c jc-sb fs12 mt8 cor-999">
<u-icon name="eye" color="#999" size="14" :label="index*7" label-color="#999" label-size="12"></u-icon>
<u-icon name="thumb-up" color="#999" size="14" :label="index*3" label-color="#999" label-size="12"></u-icon>
<text>2020-01-01 19:00</text>
</view>
</view>
</view>
</u-list-item>
</u-list>
<u-empty v-else mode="list" text="暂无数据"></u-empty>
</view>
</template>
<script>
export default {
data() {
return {
pageList: [],
total: 0,
queryParams: {
placeName: '',
pageNo: 1,
pageSize: 20
}
}
},
mounted() {
this.handleSearch()
},
methods: {
scrolltolower() {
if (this.pageList.length < this.total) {
this.queryParams.pageNo++
this.getPageList()
}
},
handleSearch() {
this.pageList = []
this.queryParams.pageNo = 1
this.getPageList()
},
getPageList() {
this.pageList = [1,2,3]
this.total = 3
// const _this = this
// const params = { ...this.queryParams }
// queryExamPoint(params).then(res => {
// _this.pageList = [..._this.pageList, ...res.data.records]
// _this.total = res.data.total
// });
},
handleClick(item) {
uni.navigateTo({
url: `/pages/course/chapter`
});
}
}
}
</script>
<style lang="scss" scoped>
.course-list {
background-color: #fff;
}
</style>

@ -0,0 +1,151 @@
<template>
<view>
<!-- 用j-navbar 组件实现导航栏 -->
<j-navbar :isBack="false" :background="{ background: '#1A8CFE' }" :extraHeight="42">
<template #left>
<view class="cor-fff">
<view class="navbar-left">
<text class="ml10 fs16">{{ title }}</text>
<text class="ml10 fs12">无人机驾考平台</text>
</view>
<view class="mt10 p5tb p15lr" style="width: 100vw">
<u-search v-model="searchValue" placeholder="搜索培训机构" :showAction="false" disabled @click="next('/pages/index/search', false)">搜索</u-search>
</view>
</view>
</template>
</j-navbar>
<view class="container" style="background: #1a8cfe">
<view class="p15tb" style="background: #1a8cfe">
<view class="flex ai-c">
<view class="text-center" style="width: 30%" @click="next('/pages/index/testRoom', true)">
<text class="custom-icon custom-icon-kaodian fs28 cor-fff"></text>
<view class="mt2 cor-fff fs12">CAAC考点</view>
</view>
<view class="text-center" style="width: 30%" @click="next('/pages/index/testCalendar', true)">
<text class="custom-icon custom-icon-kaoshishijian fs28 cor-fff"></text>
<view class="mt2 cor-fff fs12">CAAC考试时间</view>
</view>
</view>
</view>
<view class="mt5 p15 bc-fff" style="border-radius: 16px 16px 0 0">
<!-- 入驻 -->
<view class="flex jc-c ai-c">
<view class="fl1 br10" style="background: linear-gradient(to bottom, #0478d6, #01e2f3)">
<view class="flex jc-c ai-c p10" @tap="next('/pages/index/registerStation', true)">
<view class="fl1">
<view class="fs18 cor-fff">机构入驻</view>
<view class="mt12 fs12 bc-fff text-center br9" style="width: 68px; height: 18px; line-height: 18px; color: #0478d6">立即申请</view>
</view>
<image src="@/static/image/index/wrj1.png" style="width: 64px; height: 64px" />
</view>
</view>
<view class="fl1 br10 ml10" style="background: linear-gradient(to bottom, #e78728, #f2a547)">
<view class="flex jc-c ai-c p10" @tap="next('/pages/index/registerCoach', true)">
<view class="fl1">
<view class="fs18 cor-fff">教员入驻</view>
<view class="mt12 fs12 bc-fff text-center br9" style="width: 68px; height: 18px; line-height: 18px; color: #e78728">立即申请</view>
</view>
<image src="@/static/image/index/wrj2.png" style="width: 64px; height: 64px" />
</view>
</view>
</view>
<!-- 找驾校 -->
<view class="br10 mt10" style="background: linear-gradient(to bottom, #7783e4, #8ebfff)" @tap="next('/pages/index/consult', true)">
<view class="flex jc-c ai-c p10" style="height: 84px">
<view class="fl1">
<view class="fs24 cor-fff fw600">为您甄选优质驾校</view>
<view class="fs14 mt5 cor-fff">离家近 价格低 口碑好 有折扣</view>
</view>
<view class="fs16 bc-fff text-center br14 p5lr" style="height: 28px; line-height: 28px; color: #7783e4">帮我找驾校</view>
</view>
</view>
<!-- 驾校列表 -->
<view class="mt10">
<u-sticky offsetTop="120">
<view class="p10tb flex jc-sb ai-c bc-fff">
<view class="fs15 fw600">无人机驾校</view>
<view class="flex fs12 cor-333" @click="next('/pages/choseCity/choseCity', false)">
<text class="mr5">{{ queryParams.address || '全部城市' }}</text>
<u-icon name="arrow-down" />
</view>
</view>
</u-sticky>
<u-list @scrolltolower="scrolltolower" height="calc(100vh - 120px)">
<u-list-item v-for="(item, index) in pageList" :key="index">
<j-station :info="item" />
</u-list-item>
</u-list>
</view>
</view>
</view>
<u-tabbar :value="tabIndex" @change="name => changeTab(name)">
<u-tabbar-item v-for="(item, index) in tabbarList" :key="index" :text="item.text" :icon="item.icon" :name="item.name"></u-tabbar-item>
</u-tabbar>
</view>
</template>
<script>
import { getStationList } from '@/jtools/api/station';
import { mapState } from 'pinia'; //
import useUserStore from '@/jtools/store/user'; //store
export default {
computed: {
...mapState(useUserStore, ['tabbarList'])
},
data() {
return {
searchValue: '',
title: import.meta.env.VITE_APP_TITLE || '首页',
queryParams: {
address: '',
pageNo: 1,
pageSize: 20
},
total: 0,
pageList: [],
tabIndex: '/pages/index/index',
};
},
onShow() {
this.queryParams.address = uni.getStorageSync('city');
this.pageList = [];
this.queryParams.pageNo = 1;
this.getPageList();
},
methods: {
getPageList() {
const _this = this;
const params = { ...this.queryParams };
getStationList(params).then(res => {
_this.pageList = [..._this.pageList, ...res.data.records];
_this.total = res.data.total;
});
},
next(url, needLogin = false) {
if(needLogin && !useUserStore().isLogin) {
uni.showToast({title: '请先登录', icon: 'none'})
uni.navigateTo({
url: '/pages/me/login'
})
}
uni.navigateTo({
url: url
});
},
scrolltolower() {
if (this.pageList.length < this.total) {
this.queryParams.pageNo++;
this.getPageList();
}
},
changeTab(name) {
this.tabIndex = name;
uni.redirectTo({
url: name
});
}
}
};
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,13 @@
<template>
<view>我的点赞</view>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,100 @@
<template>
<view class="container">
<view class="p15">
<!-- 用户信息 -->
<view class="user-info" @tap="next('/pages/me/userInfo')">
<view class="fl1 flex ai-c">
<u-avatar size="48"></u-avatar>
<view class="ml10 fs16 fw600">
<view>{{ isLogin ? userInfo?.userName : '点击登陆' }}</view>
<view class="fs12 cor-999">{{ isLogin ? userInfo?.phone : '未登录' }}</view>
</view>
</view>
<u-icon name="arrow-right" size="16"></u-icon>
</view>
<!-- cells 列表 -->
<u-cell-group :customStyle="{ 'background-color': '#fff', 'margin-top': '10px', 'border-radius': '8px' }">
<u-cell icon="thumb-up" size="large" title="我的点赞" isLink @tap="next('/pages/me/good')"></u-cell>
<u-cell icon="heart" size="large" title="我的收藏" isLink @tap="next('/pages/me/like')"></u-cell>
<!-- <button open-type="share" class="share">
<u-cell icon="share" title="分享" size="large" isLink></u-cell>
</button> -->
<!-- <u-cell v-if="isLogin" icon="thumb-up" size="large" title="我的关注" isLink @tap="next('/pages/me/look')"></u-cell> -->
<u-cell icon="level" size="large" title="当前版本" :value="version"></u-cell>
<u-cell icon="phone" size="large" title="联系我们" :value="contactInfo.configDesc" isLink @tap="makePhone"></u-cell>
</u-cell-group>
<!-- 退出登陆 -->
<u-button v-if="isLogin" plain :customStyle="{ 'margin-top': '20px' }" @tap="logout">退出登录</u-button>
</view>
</view>
</template>
<script>
import { getSysConfig } from '@/jtools/api/index';
import { mapState, mapActions } from 'pinia'; //
import useUserStore from '@/jtools/store/user'; //store
export default {
computed: {
...mapState(useUserStore, ['userInfo', 'isLogin'])
},
data() {
return {
version: uni.getAppBaseInfo().appVersion,
contactInfo: {
configValue: '18056811878',
configDesc: '周老师'
}
};
},
onLoad() {
getSysConfig({ configKey: 'AppletContact', driverTypeId: -1 }).then(res => {
this.contactInfo = res.data;
});
},
methods: {
...mapActions(useUserStore, ['logout']),
next(url) {
if (this.isLogin) {
uni.navigateTo({
url: url
});
} else {
uni.navigateTo({
url: '/pages/me/login'
});
}
},
makePhone() {
uni.makePhoneCall({
phoneNumber: this.contactInfo.configValue
});
},
}
};
</script>
<style lang="scss" scoped>
.container {
width: 100vw;
height: 100vh;
background: linear-gradient(180deg, rgb(242, 242, 242), #fff); // 线
.user-info {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #fff;
border-radius: 8px;
padding: 15px;
}
.share {
background-color: #fff;
width: 100%;
padding: 0;
text-align: left;
border: 0;
}
}
</style>

@ -0,0 +1,13 @@
<template>
<view>我的收藏</view>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,49 @@
<template>
<view class="content">
<view class="mb40" style="border-bottom: 1px solid #eee;margin-top: 100px;">
<u-input border="none" v-model="loginForm.phone" type="number" maxlength="11"
placeholder="输入手机号" />
</view>
<u-button shape="circle" type="primary" @click="bindLogin()">授权登录</u-button>
</view>
</template>
<script>
import useUserStore from '@/jtools/store/user'
import storage from '@/jtools/storage';
export default {
data() {
return {
loginForm: {
phone: '',
code: '000000'
},
countDown: 0,
js: undefined
};
},
methods: {
bindLogin() {
let params = { ...this.loginForm }
if (storage.get('companyId')) {
params.id = storage.get('companyId')
}
useUserStore().login(params).then(resp => {
if (resp.userId) {
useUserStore().getUserInfo()
uni.navigateBack()
}
})
},
}
}
</script>
<style>
.content {
width: 100vw;
height: 100vh;
padding: 20px;
background-color: #fff;
}
</style>

@ -0,0 +1,15 @@
<template>
<div>
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,115 @@
<template>
<view class="container">
<view class="flex ai-c jc-sb p15lr p5tb bb1">
<view class="fs14 fl1">头像</view>
<u-avatar size="56" :src="form.avatar" @tap="chooseAvatar" />
</view>
<view class="flex ai-c jc-sb p15 bb1">
<view class="fs14">
<text style="color: red;">*</text>
<text class="ml2">昵称</text>
</view>
<input
type="text"
v-model="form.userName"
placeholder="请输入昵称"
style="width: 100px;text-align: right; flex: 1;"
/>
</view>
<view class="flex ai-c jc-sb p15 bb1">
<view class="fs14">真实姓名</view>
<input
type="text"
v-model="form.userName"
placeholder="请输入真实姓名"
style="width: 100px;text-align: right; flex: 1;"
/>
</view>
<view class="flex ai-c jc-sb p15 bb1">
<view class="fs14">性别</view>
<view class="flex" @tap="showPicker">
<text class="cor-666">{{ form.sex || '请选择性别' }}</text>
<u-icon name="arrow-right" size="16" class="ml10"></u-icon>
</view>
</view>
<view class="flex ai-c jc-sb p15 bb1">
<view class="fs14">生日</view>
<view class="flex" @tap="showPicker">
<text class="cor-666">{{ form.birthday || '请选择出生日期' }}</text>
<u-icon name="arrow-right" size="16" class="ml10"></u-icon>
</view>
</view>
<view class="flex ai-c jc-sb p15 bb1">
<view class="fs14 fl1">手机号码</view>
<text class="cor-999">{{ form.phone }}</text>
</view>
<!-- 保存 -->
<view class="p15 mt20">
<u-button type="primary" @tap="submit">保存</u-button>
</view>
</view>
</template>
<script>
import { updateUserInfo } from '@/jtools/api/user'
import useUserStore from '@/jtools/store/user' //store
export default {
name: 'UserInfo',
data() {
return {
form: {...useUserStore().userInfo},
saving: false,
}
},
methods: {
async submit() {
const res = await updateUserInfo(this.form)
if (res.code == '0000') {
uni.showToast({
title: '修改成功',
icon: 'none'
})
useUserStore().setUserInfo(this.form)
uni.navigateBack()
}
},
chooseAvatar() {
const _this = this
uni.chooseImage({
count: 1,
success: (res) => {
_this.form.avatar = res.tempFilePaths[0]
_this.saving = true
_this.uploadAvatar(res.tempFilePaths[0])
}
})
},
uploadAvatar(url) {
uni.uploadFile({
url: import.meta.env.VITE_UPLOAD_URL + 'wrj-api/system/file/upload', //
filePath: url,
name: 'file',
success: (res) => {
// setTimeout(() => {
// resolve(JSON.parse(res.data).data);
// }, 100);
this.saving = false;
},
fail: () => {
resolve(null);
this.saving = false;
}
});
}
}
}
</script>
<style lang="scss" scoped>
.container {
width: 100vw;
height: 100vh;
background-color: #fff;
}
</style>

@ -0,0 +1,13 @@
<template>
<u-empty mode="page">
<u-button slot="bottom" size="medium" @click="$Router.pushTab('/pages/tabbar/home')">
去首页
</u-button>
</u-empty>
</template>
<script></script>
<style></style>

@ -0,0 +1,27 @@
#选择城市,城市搜索弹窗
## 引入
import choseCity from "@/components/chose-city/chose-city"
export default {
components: {
choseCity
},
data() {
return {
showCity:true // 默弹窗显示
}
},
methods: {
selectCity(item) {
console.log('-您选择的城市-',item)
// this.showCity = false
},
closeModal() {
this.showCity = false
},
}
}
## 页面引用
<chose-city @selectCity="selectCity" v-if="showCity" @closeModal="closeModal"></chose-city>
showCity:弹窗是否显示
selectCity:选择后的回调
closeModal:点击取消的回调

File diff suppressed because it is too large Load Diff

@ -0,0 +1,47 @@
@font-face {
font-family: "custom-icon"; /* Project id 4964828 */
src: url('iconfont.woff2?t=1752573741103') format('woff2'),
url('iconfont.woff?t=1752573741103') format('woff'),
url('iconfont.ttf?t=1752573741103') format('truetype');
}
.custom-icon {
font-family: "custom-icon" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.custom-icon-kaodian:before {
content: "\e601";
}
.custom-icon-kaoshishijian:before {
content: "\e614";
}
.custom-icon-wodeshoucang:before {
content: "\e699";
}
.custom-icon-wodecuoti:before {
content: "\e64a";
}
.custom-icon-qicheqianlian-:before {
content: "\e611";
}
.custom-icon-feiji:before {
content: "\e6a9";
}
.custom-icon-helicopter-full:before {
content: "\e9dc";
}
.custom-icon-wurenji:before {
content: "\e872";
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,65 @@
{
"id": "4964828",
"name": "无人机",
"font_family": "custom-icon",
"css_prefix_text": "custom-icon-",
"description": "",
"glyphs": [
{
"icon_id": "1894917",
"name": "机构-考点",
"font_class": "kaodian",
"unicode": "e601",
"unicode_decimal": 58881
},
{
"icon_id": "7316903",
"name": "考试时间",
"font_class": "kaoshishijian",
"unicode": "e614",
"unicode_decimal": 58900
},
{
"icon_id": "908562",
"name": "我的收藏",
"font_class": "wodeshoucang",
"unicode": "e699",
"unicode_decimal": 59033
},
{
"icon_id": "9570813",
"name": "我的错题",
"font_class": "wodecuoti",
"unicode": "e64a",
"unicode_decimal": 58954
},
{
"icon_id": "2038774",
"name": "飞机-02",
"font_class": "qicheqianlian-",
"unicode": "e611",
"unicode_decimal": 58897
},
{
"icon_id": "17427028",
"name": "飞机",
"font_class": "feiji",
"unicode": "e6a9",
"unicode_decimal": 59049
},
{
"icon_id": "18170586",
"name": "直升机",
"font_class": "helicopter-full",
"unicode": "e9dc",
"unicode_decimal": 59868
},
{
"icon_id": "36636166",
"name": "无人机",
"font_class": "wurenji",
"unicode": "e872",
"unicode_decimal": 59506
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

@ -0,0 +1,3 @@
page {
color: $uni-color-primary;
}

@ -0,0 +1,434 @@
/*每个页面公共css */
page {
background: #f4f4f4;
color: #383838;
font-size: 26rpx;
}
/* image{
background: skyblue;
} */
image,
video {
display: block;
}
image{will-change: transform}
/*字体颜色*/
.cor_fff {
color: #fff;
}
.cor_000 {
color: #000;
}
.cor_red {
color: #eb3831;
}
.cor_333 {
color: #333;
}
.cor_666 {
color: #666;
}
.cor_999 {
color: #999;
}
.cor_ccc {
color: #ccc;
}
.cor_eee{
color: #eee;
}
.cor_text {
color: #262626;
}
.cor_38{
color: #383838;
}
.cor_8c {
color: #8c8c8c;
}
.cor_80 {
color: #808080;
}
.cor_A6 {
color: #A6A6A6;
}
.cor_theme {
color: $uni-color-primary !important;
}
.cor_blue {
color: #43aefd;
}
.bk_theme {
background-color: $uni-color-primary !important;
color: #fff !important;
}
.fwb {
font-weight: bold;
}
/**图片大小**/
.img16 {
width: 16rpx;
height: 16rpx;
}
.mt70 {
margin-top: 70rpx;
}
.mt80 {
margin-top: 80rpx;
}
.mt90 {
margin-top: 90rpx;
}
.mr60 {
margin-right: 60rpx;
}
.mr70 {
margin-right: 70rpx;
}
.mr100 {
margin-right: 100rpx;
}
.m20lr {
margin-left: 20rpx;
margin-right: 20rpx;
}
.m30lr {
margin-left: 30rpx;
margin-right: 30rpx;
}
/**border**/
.bb1 {
border-bottom: solid 1px #f4f4f4;
}
.bt1 {
border-top: solid 1px #eee;
}
.border1 {
border: solid 1px #eee;
}
.border_tb1{
border-bottom: solid 1px #eee;
border-top: solid 1px #eee;
}
/***背景**/
.bk_f {
background-color: #fff;
}
.bk_f9 {
background-color: #f9f9f9;
}
.bk_f2 {
background-color: #f2f2f2;
}
.bk_rgba05 {
background-color: rgba(0, 0, 0, 0.5);
}
.bk_main {
background-color: #3d92e1;
}
.bk_blue {
background: skyblue;
}
.bk_red {
background-color: #ff2d17;
}
.bk_white {
background-color: #fff;
}
.opt5 {
opacity: 0.5;
}
.br_p50 {
border-radius: 50%;
}
.br_ltb{
border-radius: 50% 0 0 50% !important;
}
.br_rtb{
border-radius: 0 50% 50% 0 !important;
}
/**宽度**/
.wp20 {
width: 20%;
}
.wp25 {
width: 25%;
}
.wp40 {
width: 40%;
}
.wp50 {
width: 50%;
}
.wp100 {
width: 100%;
}
/**高度**/
.hp100 {
height: 100%;
}
/**行距**/
.lh1 {
line-height: 1;
}
.lh50{
line-height: 50rpx;
}
/**flex 设置**/
.df {
display: flex;
}
.fldr {
flex-direction: row !important;
}
.fldc {
flex-direction: column !important;
}
.fldrr {
flex-direction: row-reverse !important;
}
.jcsb {
justify-content: space-between !important;
}
.jcsba {
justify-content: space-around !important;
}
.jcfs {
justify-content: flex-start !important;
}
.jcc {
justify-content: center !important;
}
.jcfe {
justify-content: flex-end;
}
.fl1 {
flex: 1 !important;
}
.fw {
flex-wrap: wrap;
}
.ai-center {
align-items: center !important;
}
.ai-start {
align-items: flex-start;
}
.ai-baseline {
align-items: flex-end;
}
.ai-end {
align-items: flex-end;
}
.fls0 {
flex-shrink: 0;
}
/*对齐*/
.tac {
text-align: center;
}
.tar {
text-align: right;
}
.tal {
text-align: left;
}
/** 其他样式 **/
.ov {
overflow: hidden;
}
.ovya {
overflow-y: auto;
}
.bsb {
box-sizing: border-box;
}
.re {
position: relative;
}
.ab {
position: absolute;
}
.ab_full {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.fixed {
position: fixed;
}
.db {
display: block;
}
.di {
display: inline;
}
.dib {
display: inline-block;
}
.dif {
display: inline-flex;
}
.v-middle {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.h-middle {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.middle {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.text-line-through {
text-decoration: line-through;
}
.text-underline {
text-decoration: underline;
}
.wb {
display: block;
word-break: break-all;
word-wrap: break-word;
}
.vam {
vertical-align: middle;
}
/*灰色分割条*/
.gray_bar {
height: 20rpx;
background: #f5f5f5;
}
.scroll-box {
flex: 1;
height: 100%;
position: relative;
}
.content_box {
flex: 1;
overflow-y: auto;
}
.theme-tag {
display: inline-block;
padding: 0 25rpx;
height: 36rpx;
line-height: 36rpx;
border-radius: 20rpx;
border: 2rpx solid $uni-color-primary;
box-sizing: content-box;
font-size: 24rpx;
color: $uni-color-primary;
}
.selector {
position: relative;
padding-left: 30rpx;
padding-right: 20rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 30rpx;
font-size: 24rpx;
color: #333;
background-color: #fff;
}
.selector.actived {
color: $uni-color-primary;
border: 1rpx solid $uni-color-primary;
background: #e6f2ff;
}
.img24 {
width: 24px;
height: 24px;
}
.img36 {
width: 36px;
height: 36px;
}
.img48 {
width: 48px;
height: 48px;
}
.img50 {
width: 50px;
height: 50px;
}
.img120 {
width: 120px;
height: 120px;
}
.img140 {
width: 140px;
height: 140px;
}
.theme-bg-light {
background-color: $uni-color-primary;
}
.pt100 {
padding-top: 100rpx;
}
.theme-btn {
padding: 0 20px;
min-width: 250rpx;
height: 78rpx;
line-height: 80rpx;
text-align: center;
font-size: 28rpx;
color: #fff;
border-radius: 40rpx;
background-color: $uni-color-primary;
}
.theme-btn-light {
padding: 0 20px;
min-width: 250rpx;
height: 78rpx;
line-height: 80rpx;
text-align: center;
font-size: 28rpx;
color: #fff;
border-radius: 40rpx;
color: $uni-color-primary;
border: 1px solid $uni-color-primary;
}
.img70 {
width:70px;
height: 70px;
}
// .u-tabbar {
// max-height: 50px;
// }
.j-tag {
display: inline-block;
margin-right: 2px;
}

@ -0,0 +1,674 @@
html,
body,
#app {
box-sizing: border-box;
height: 100%;
font-family: Avenir, Helvetica, Arial, sans-serif;
}
/* 背景 */
.bc-000 {
background-color: #000;
}
.bc-f5 {
background-color: #f5f5f5;
}
.bc-f7 {
background-color: #f7f7f7;
}
.bc-fff {
background-color: #fff;
}
.bc-t {
background-color: transparent;
}
/* border */
.border-top {
border-top: solid 1px #eee;
}
.border-bottom {
border-bottom: solid 1px #eee;
}
.border-0 {
border: 0px;
}
.border-1 {
border: 1px solid #eee;
}
.border-2 {
border: 2px solid #eee;
}
.border-3 {
border: 3px solid #eee;
}
.border-4 {
border: 4px solid #eee;
}
.border-5 {
border: 5px solid #eee;
}
/* 文本格式 */
.i {
word-wrap: break-word;
text-align: justify;
text-justify: inter-ideograph;
}
/* 字体颜色 */
.cor-000 {
color: #000000;
}
.cor-333 {
color: #333333;
}
.cor-666 {
color: #666666;
}
.cor-999 {
color: #999999;
}
.cor-aaa {
color: #aaaaaa;
}
.cor-ccc {
color: #cccccc;
}
.cor-ddd {
color: #dddddd;
}
.cor-fff {
color: #ffffff;
}
.cor-price {
color: #C03639;
}
/* 行距 */
.lh10 {
line-height: 1;
}
.lh11 {
line-height: 1.1;
}
.lh12 {
line-height: 1.2;
}
.lh13 {
line-height: 1.3;
}
.lh14 {
line-height: 1.4;
}
.lh15 {
line-height: 1.5;
}
.lh16 {
line-height: 1.6;
}
.lh18 {
line-height: 1.8;
}
.lh20 {
line-height: 2;
}
.lh25 {
line-height: 2.5;
}
.lh30 {
line-height: 3;
}
/* 高度 */
.hvh100 {
height: 100vh;
overflow: hidden;
}
.hp100 {
height: 100%;
overflow: hidden;
}
/* float */
.float-l {
float: left;
}
.float-r {
float: right;
}
.clearfix-both::after {
display: table;
clear: both;
content: '';
}
/* flex */
.flex {
display: flex;
}
.fl0 {
flex: 0;
}
.fl1 {
flex: 1;
}
.fl2 {
flex: 2;
}
.fl3 {
flex: 3;
}
.fl4 {
flex: 4;
}
.fld-r {
flex-direction: row;
}
.fld-c {
flex-direction: column;
}
.fld-rr {
flex-direction: row-reverse;
}
.flw-w {
flex-wrap: wrap;
}
.fls0 {
flex-shrink: 0;
}
.jc-sb {
justify-content: space-between;
}
.jc-sa {
justify-content: space-around;
}
.jc-fs {
justify-content: flex-start;
}
.jc-c {
justify-content: center;
}
.jc-fe {
justify-content: flex-end;
}
.ai-c {
align-items: center;
}
.ai-s {
align-items: stretch;
}
.ai-fs {
align-items: flex-start;
}
.ai-fe {
align-items: flex-end;
}
/* 对齐 */
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.text-left {
text-align: left;
}
/* display */
.none {
display: none;
}
.block {
display: block;
}
.inline {
display: inline;
}
.inline-block {
display: inline-block;
}
/* 溢出 */
.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.text-ellipsis2 {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.text-ellipsis3 {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
/* 其他 */
.middle {
position: absolute;
z-index: 2;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.middle-y {
position: absolute;
z-index: 2;
top: 50%;
transform: translateY(-50%);
}
.middle-x {
position: absolute;
z-index: 2;
left: 50%;
transform: translateX(-50%);
}
.text-line-through {
text-decoration: line-through;
}
.text-underline {
text-decoration: underline;
}
.overflow-h {
overflow: hidden;
}
.overflow-y {
overflow-y: auto;
overflow-x: hidden;
}
.overflow-x {
overflow-x: auto;
overflow-y: hidden;
}
.border-box {
box-sizing: border-box;
}
.content-box {
box-sizing: content-box;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.absolute-full {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.fixed {
position: fixed;
}
.break-all {
display: block;
word-break: break-all;
word-wrap: break-word;
}
.nowrap {
white-space: nowrap;
}
.va-m {
vertical-align: middle;
}
.va-t {
vertical-align: top;
}
.va-b {
vertical-align: bottom;
}
.opacity0 {
opacity: 0;
}
.opacity1 {
opacity: 0.1;
}
.opacity2 {
opacity: 0.2;
}
.opacity3 {
opacity: 0.3;
}
.opacity4 {
opacity: 0.4;
}
.opacity5 {
opacity: 0.5;
}
.opacity6 {
opacity: 0.6;
}
.opacity7 {
opacity: 0.7;
}
.opacity8 {
opacity: 0.8;
}
.opacity9 {
opacity: 0.9;
}
.opacity10 {
opacity: 1;
}
/* z-index */
.z-index-1 {
z-index: -1;
}
.z-index0 {
z-index: 0;
}
.z-index1 {
z-index: 1;
}
.z-index2 {
z-index: 2;
}
.z-index3 {
z-index: 3;
}
.z-index4 {
z-index: 4;
}
.z-index5 {
z-index: 5;
}
.z-index6 {
z-index: 6;
}
.z-index7 {
z-index: 7;
}
.z-index8 {
z-index: 8;
}
.z-index9 {
z-index: 9;
}
.z-index10 {
z-index: 10;
}
.z-index15 {
z-index: 15;
}
.z-index19 {
z-index: 19;
}
.z-index20 {
z-index: 20;
}
.z-index99 {
z-index: 99;
}
.z-index100 {
z-index: 100;
}
.z-index300 {
z-index: 300;
}
.z-index500 {
z-index: 500;
}
.z-index999 {
z-index: 999;
}
/* 宽度 */
.wp20 {
width: 20%;
}
.wp23 {
width: 23%;
}
.wp24 {
width: 24%;
}
.wp25 {
width: 25%;
}
.wp30 {
width: 30%;
}
.wp31 {
width: 31%;
}
.wp32 {
width: 32%;
}
.wp33 {
width: 33%;
}
.wp40 {
width: 40%;
}
.wp45 {
width: 45%;
}
.wp48 {
width: 48%;
}
.wp49 {
width: 49%;
}
.wp50 {
width: 50%;
}
.wp60 {
width: 60%;
}
.wp70 {
width: 70%;
}
.wp80 {
width: 80%;
}
.wp85 {
width: 85%;
}
.wp90 {
width: 90%;
}
.wp95 {
width: 95%;
}
.wp100 {
width: 100%;
}
.wvw100 {
width: 100vw;
}
.wp100-i {
width: 100% !important;
}
/* 字体样式 */
.arial {
font-family: Arial;
}
/* 字体大小 */
@for $i from 0 through 100 {
.fs#{$i} {
font-size: $i + px;
}
.img#{$i} {
width: $i + px;
height: $i + px;
}
}
.fw400 {
font-weight: 400;
}
.fw500 {
font-weight: 500;
}
.fw600 {
font-weight: 600;
}
.fw700 {
font-weight: 700;
}
.fw800 {
font-weight: 800;
}
.fw900 {
font-weight: 900;
}
.m0a {
margin: 0 auto;
}
.br-p50 {
border-radius: 50%;
}
/* marging */
.mt-3 {
margin-top: -3px;
}
.mt-2 {
margin-top: -2px;
}
.mt-1 {
margin-top: -1px;
}
.mr-3 {
margin-right: -3px;
}
.mr-2 {
margin-right: -2px;
}
.mr-1 {
margin-right: -1px;
}
.mr-p1 {
margin-right: 1%;
}
.mr-p2 {
margin-right: 2%;
}
.mr-p3 {
margin-right: 3%;
}
.mr-p4 {
margin-right: 4%;
}
.mr-p5 {
margin-right: 5%;
}
.mb-5 {
margin-bottom: -5px;
}
.mb-4 {
margin-bottom: -4px;
}
.mb-3 {
margin-bottom: -3px;
}
.mb-2 {
margin-bottom: -2px;
}
.mb-1 {
margin-bottom: -1px;
}
.ml-5 {
margin-left: -5px;
}
.ml-4 {
margin-left: -4px;
}
.ml-3 {
margin-left: -3px;
}
.ml-2 {
margin-left: -2px;
}
.ml-1 {
margin-left: -1px;
}
@for $i from 0 through 50 {
.m#{$i} {
margin: $i + px;
}
.mt#{$i} {
margin-top: $i + px;
}
.ml#{$i} {
margin-left: $i + px;
}
.mb#{$i} {
margin-bottom: $i + px;
}
.mr#{$i} {
margin-right: $i + px;
}
.m#{$i}tb {
margin-top: $i + px;
margin-bottom: $i + px;
}
.m#{$i}lr {
margin-left: $i + px;
margin-right: $i + px;
}
.p#{$i} {
padding: $i + px;
}
.pt#{$i} {
padding-top: $i + px;
}
.pl#{$i} {
padding-left: $i + px;
}
.pb#{$i} {
padding-bottom: $i + px;
}
.pr#{$i} {
padding-right: $i + px;
}
.p#{$i}lr {
padding-left: $i + px;
padding-right: $i + px;
}
.p#{$i}tb {
padding-top: $i + px;
padding-bottom: $i + px;
}
.br#{$i} {
border-radius: $i + px;
}
}
@for $i from -10 through 20 {
.top#{$i} {
top: $i + px;
}
.left#{$i} {
left: $i + px;
}
.right#{$i} {
right: $i + px;
}
.bottom#{$i} {
bottom: $i + px;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,129 @@
@import '@/static/style/colorui.css';
@import './base.scss';
@import './app.scss';
page {
-webkit-overflow-scrolling: touch; // ios滑动不流畅
height: 100%;
background: #f6f6f6;
width: 100%;
font-size: 30rpx;
font-family: Arial;
word-break: break-all; //英文文本不换行
white-space: normal;
color: $u-main-color;
// padding-bottom: constant(safe-area-inset-bottom);
// padding-bottom: env(safe-area-inset-bottom);
// box-sizing: content-box;
}
/* #ifdef MP-WEIXIN || APP-PLUS */
::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
color: transparent;
}
/* #endif */
uni-tabbar.uni-tabbar-bottom .uni-tabbar {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
box-sizing: content-box;
}
.box-safe-area {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
box-sizing: content-box;
}
.u-tabs .uni-scroll-view::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
display: none !important;
}
.content-wrapper {
padding: 25rpx;
}
.input-placeholder {
font-size: 28rpx !important;
color: #a6a6a6 !important;
font-weight: 400 !important;
}
.uni-input-input, .uni-textarea-textarea, {
font-size: 28rpx;
color: #333;
}
.value-form .u-form-item__body {
flex-wrap: wrap !important;
align-items: flex-start !important;
}
.u-form-item__body__right__message {
margin-left: 0 !important;
text-align: center;
}
.u-form-item__body {
padding-top: 30rpx !important;
padding-bottom: 30rpx !important;
.u-form-item__body__left {
margin-right: 30rpx !important;
}
.u-form-item__body__right {
font-size: 28rpx !important;
}
}
.form-label {
font-size: 28rpx;
color: #666;
line-height: 1;
}
.form-value {
font-size: 28rpx;
color: #000;
line-height: 40rpx;
}
.u-tabs__wrapper__nav__line {
bottom: 0 !important;
}
.u-notice .uicon-volume {
font-size: 40rpx !important;
}
.uni-modal__btn_primary {
color: $uni-color-primary !important;
}
.u-empty__text {
font-size: 30rpx !important;
}
.notice .u-icon__icon {
color: $uni-color-primary !important;
}
.u-popup {
flex: none !important;
.u-popup__content__close {
padding: 15rpx;
}
}
input[type='password'] {
font-size: 14px;
}
rich-text {
display: block !important;
font-size: 13px;
white-space: pre-wrap;
line-height: 1.8;
padding: 5px 0;
}

@ -0,0 +1,26 @@
@charset "UTF-8";
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
/* 文字基本颜色 */
/* 背景颜色 */
/* 边框颜色 */
/* 尺寸变量 */
/* 文字尺寸 */
/* 图片尺寸 */
/* Border Radius */
/* 水平间距 */
/* 垂直间距 */
/* 透明度 */
/* 文章场景相关 */

1
src/uni.min.css vendored

@ -0,0 +1 @@
page{padding-top:0 !important}.uni-page-head{display:none !important}

@ -0,0 +1,76 @@
/**
* 这里是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;

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 https://uiadmin.net/uview-plus
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.

@ -0,0 +1,74 @@
<p align="center">
<img alt="logo" src="https://uiadmin.net/uview-plus/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/jry/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-x(即将发布)的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水。uview-plus是基于uView2.x移植的支持vue3的版本,感谢uView。
## 可视化设计
uview-plus现已推出免费可视化设计,可以方便的进行页面可视化设计,导出源码即可使用。极大提高前端页面开发效率;如产品经理设计师直接使用更可作为高保真高可用原型制作工具,让设计稿即代码,无需传统的设计稿开发还原步骤。
<img src="https://s3.bmp.ovh/imgs/2024/11/24/fd58d00071e6e5df.png" width="900" height="auto" >
<img src="https://s3.bmp.ovh/imgs/2024/11/24/8e85a519fe627fb1.png" width="900" height="auto" >
## 文档
[官方文档:https://uview-plus.jiangruyi.com](https://uview-plus.jiangruyi.com)
[备用文档:https://uiadmin.net/uview-plus](https://uiadmin.net/uview-plus)
## 预览
您可以通过**微信**扫码,查看最佳的演示效果。
<br>
<br>
<img src="https://uview-plus.jiangruyi.com/common/h5_qrcode.png" width="220" height="220" >
## 链接
- [官方文档](https://uview-plus.jiangruyi.com)
- [更新日志](https://uview-plus.jiangruyi.com/components/changelog.html)
- [升级指南](https://uview-plus.jiangruyi.com/components/changeGuide.html)
- [关于我们](https://uview-plus.jiangruyi.com/cooperation/about.html)
## 交流反馈
欢迎加入我们的QQ群交流反馈:[点此跳转](https://uview-plus.jiangruyi.com/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://uview-plus.jiangruyi.com/components/install.html)了解更详细的内容
## 快速上手
请通过[快速上手](https://uview-plus.jiangruyi.com/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应用到您的产品中。

@ -0,0 +1,909 @@
## 3.4.48(2025-07-10)
fix: 官方文档Card示例组件多行显示省略号样式异常
feat: album组件支持自定义preview事件
## 3.4.47(2025-07-09)
fix: 修复datetime-picker打开时,数值可能出现对不上的问题
feat: Subsection 分段器添加支持从 list中读取激活文字颜色和未激活文字颜色
fix: 修复modal定义confirmButton
fix: safe-bottom底部安全距离在小程序优先用JS计算
feat: 新增tree树形组件
## 3.4.46(2025-07-08)
feat: 上传组件预览视频支持videoPreviewObjectFit参数
feat: td增加多个样式props
fix: 修复noticeBar字号增大时文字遮挡
feat: 项目工程增加pinia
## 3.4.45(2025-07-01)
fix: 修复picker-data组件缺少name
fix: 优化picker高度单位
## 3.4.44(2025-06-30)
fix: 修复indexList中stikcy属性写死的问题(always true)
feat: 搜索框添加新的右侧插槽
fix: 修复indexList中丢失的select event
fix: 解决因为层级问题导致点击picker选择器无法正常弹出
## 3.4.43(2025-06-16)
feat: table2支持header插槽
## 3.4.42(2025-06-12)
fix: 修复qrcode中默认id问题及canvas2时App无法绘制二维码 感谢@jiaruiyan
## 3.4.41(2025-06-11)
feat: qrcode支持 新参数useRootHeightAndWidth 是否使用根节点的宽高 感谢@YJR
feat: toast支持设置zIndex层级
## 3.4.40(2025-06-06)
fix: 升级二维码 canvas -> canvas2 感谢@yjr
## 3.4.39(2025-05-31)
fix: 修改步骤条微信小程序下的布局 感谢@jiaruiyan
fix: u-tabs在屏幕尺寸发生变化时滑块位置没有发生变化 感谢@aqzhft
fix: 鸿蒙平台不支持plus.runtime.openWeb 感谢@aqzhft
## 3.4.38(2025-05-30)
fix: 修复picker-data快捷组件缺少index
fix: 修复picker组件双向绑定初始化及取消后复原再次打开后的当前项目
## 3.4.37(2025-05-29)
feat: modal支持设置动画时间
fix: DatetimePicker v-model 绑定异步设置无效 (#803)
## 3.4.36(2025-05-28)
fix: lazy-load图片为空时显示错误
## 3.4.35(2025-05-28)
feat: 进度条支持从右往左加载
## 3.4.34(2025-05-28)
feat: table2支持自定义标题和单元格样式
## 3.4.33(2025-05-27)
fix: 修复小程序cate-tab第一次切换时没反应 感谢@jiaruiyan
fix: 修复datetimepicker传入空字符串时导致组件崩溃 感谢@jiaruiyan
fix: 修复album带单位的字符串参与计算导致的计算数据错误 感谢@jiaruiyan
## 3.4.32(2025-05-26)
feat: 增加状态栏独立颜色配置支持支付宝小程序状态栏对背景色识别的不友好的情况
fix: 抖音二维码兼容修复
feat: cate-tab组件增加rightTop插槽 #715
fix: 修改 test.promise(res) 预期结果不一致
## 3.4.31(2025-05-17)
fix: 修复parse富文本组件导致鸿蒙运行白屏
fix: 去除演示项目中uni.$u用法便于兼容鸿蒙
feat: modal新增popupBottom插槽适用类似关闭按钮与内容区域分离的场景
## 3.4.30(2025-05-16)
feat: 新增pagination分页器组件
feat: popup新增bottom插槽适用类似关闭按钮与内容区域分离的场景
## 3.4.29(2025-05-15)
fix: 修复table2横向滚动样式
fix: 修复table2组件宽度兼容
fix: 修复image显示png图片时默认背景色问题
feat: cate-tab新增height参数便于设置组件高度
feat: 在index.js种导出digit.js便于使用
fix: 修复tag组件缺失iconColor属性
fix: 优化index-list的setValueForTouch方法逻辑 #708
feat: number-box支持change事件返回变动是点击了增加还是减少按钮
fix: 修复table2在小程序下部分情形不显示表格
## 3.4.28(2025-05-12)
feat: 新增table表格组件
feat: 新增element-plus风格的table2组件
## 3.4.27(2025-05-06)
fix: 修复card组件props
## 3.4.26(2025-05-06)
fix: 修复test工具引入
feat: card组件支持全局设置props默认值
fix: 修复image在加载错误情况下高度和宽度不正确问题
fix: 修复picker-data快捷组件默认picker选中
fix: 修复日历month子组件缺失emits定义
## 3.4.25(2025-04-27)
fix: up-form编译在微信小程序里样式缺失 #640
fix: number-box输入为空时自动设为最小值
feat: picker与datetimepicke组件hasInput模式支持inputProps属性
## 3.4.24(2025-04-25)
fix: 修复upload上传逻辑(感谢@semdy)
## 3.4.23(2025-04-24)
chore: 补全chooseFile TS类型(感谢@semdy)
feat: u-search组件的图标支持显示在右边(感谢@semdy)
chore: 修正chooseFile返回的数据TS类型(感谢@semdy)
fix: PR导致缺失name影响uplad自动上传扩展名
## 3.4.22(2025-04-22)
fix: 修复自动上传偶发的success被覆盖为uploading
fix: float-button缺少key #677
fix: upload组件完善优化(感谢@semdy)
fix: toolbar组件confirmColor属性默认改为空,以便默认使用主题色、标题字体加粗(感谢@semdy)
## 3.4.21(2025-04-21)
feat: subsection分段器支持双向绑定current
feat: select组件支持maxHeight属性
feat: datetime-picker支持inputBorder属性
## 3.4.20(2025-04-17)
fix: 修复navbar-mini提示border不存在
feat: status-bar支持对外暴露状态栏高度值
feat: upload支持自定义自动上传后处理逻辑便于对接不同规范后端
feat: 优化tag组件插槽
## 3.4.19(2025-04-14)
fix: 修复model组件增加contentStyle带来的语法问题
## 3.4.18(2025-04-14)
fix: upload组件支持所有文件类型的onClickPreview事件
## 3.4.17(2025-04-11)
feat: select组件text插槽增加scope传递currentLabel
## 3.4.16(2025-04-10)
fix: 修复安卓新加载字体方式导致Cannot read property '$page' of undefined
## 3.4.15(2025-04-10)
improvment: 优化移步加载数据时swiper组件displayMultipleItems报错
feat: modal增加contentStyle属性
fix: 修复下拉菜单收起动画缺失
fix: 修复sticky的offset属性值为响应式数据时失效 #237
## 3.4.14(2025-04-09)
feat: 支持自托管内置图标及扩展自定义图标
## 3.4.13(2025-04-08)
fix: tabs点击当前tab触发change事件
## 3.4.12(2025-04-02)
fix: dropdown关闭后遮挡页面内容 #653
fix u-sticky.vue Uncaught TypeError: e.querySelector is not a function at uni-app-view.umd.js
## 3.4.11(2025-03-31)
fix: 优化upload组件预览视频的弹窗占位
## 3.4.10(2025-03-28)
feat: select组件新增多个props属性及优化
fix: 修复cate-tab报错index is not defined #661
## 3.4.9(2025-03-27)
fix: 修复upload组件split报错
fix: 修复float-button缺少flex样式
## 3.4.8(2025-03-27)
fix: 修复upload组件split报错
fix: 移除mapState
## 3.4.7(2025-03-26)
fix: 修复action-sheet-data和picker-data数据回显
fix: 优化upload组件视频封面兼容
## 3.4.6(2025-03-25)
feat: checkbox触发change时携带name参数
feat: upload组件支持服务器本机和阿里云OSS自动上传功能及上传进度条
feat: upload组件支持视频预览及oss上传时获取视频封面图
feat: 新增up-action-sheet-data快捷组件
feat: 新增up-picker-data快捷组件
## 3.4.5(2025-03-24)
feat: tag组件新增textSize/height/padding/borderRadius属性
feat: 新增genLightColor自动计算浅色方法及tag组件支持autoBgColor自动计算背景色
## 3.4.4(2025-03-13)
feat: modal增加异步操作进行中点击取消弹出提示特性防止操作被中断
fix: 修复toast组件show方法类型声明
## 3.4.3(2025-03-12)
fix: 修复textarea自动增高时在输入时高度异常
## 3.4.2(2025-03-11)
feat: step组件增加title插槽及增加辅助class便于自定义样式
## 3.4.1(2025-03-11)
feat: 新机制确保setConfig与http在nvue等环境下生效
## 3.3.74(2025-03-06)
fix: CateTab语法问题
## 3.3.73(2025-03-06)
feat: CateTab新增v-model:current属性
## 3.3.72(2025-02-28)
feat: tabs组件支持icon图标及插槽
## 3.3.71(2025-02-27)
feat: 折叠面板collapse增加titileStyle/iconStyle/rightIconStyle属性
feat: 折叠面板组件新增cellCustomStyle/cellCustomClass属性
fix: select组件盒模型
## 3.3.70(2025-02-24)
fix: 修改u-checkbox-group组件changes事件发生位置
## 3.3.69(2025-02-19)
picker允许传递禁用颜色props
slider组件isRange状态下增加min max插槽分开显示内容
feat: 新增经典下拉框组件up-select
## 3.3.68(2025-02-12)
fix: 修复weekText类型
feat: 日历增加单选与多选指定禁止选中的日期功能
fix: NumberBox删除数字时取值有误 #613
## 3.3.67(2025-02-11)
feat: navbar支持返回全局拦截器配置
feat: 表单-校验-支持无提示-得到校验结果
feat: picker传递hasInput属性时候,可以禁用输入框点击
## 3.3.66(2025-02-09)
feat: steps-item增加content插槽
## 3.3.65(2025-02-05)
feat: number-box组件新增按钮圆角/按钮宽度/数据框背景色/迷你模式
## 3.3.64(2025-01-18)
feat: 日历组件支持自定义星期文案
## 3.3.63(2025-01-13)
fix: cate-tab支持支付宝小程序
fix: textarea 修复 placeholder-style
fix: 修复在图片加载及加载失败时容器宽度
fix: waterfall组件报错Maximum recursive updates
## 3.3.62(2025-01-10)
feat: sleder滑动选择器双滑块增加外层触发值的变动功能
fix: picker支持hasInput优化
## 3.3.61(2024-12-31)
fix: 修复微信getSystemInfoSync接口废弃警告
fix: 'u-status-bar' symbol missing
## 3.3.60(2024-12-30)
feat: 日期组件支持禁用
fix: ts定义修复 #600
feat: Tabs组件选中时增加一个active的class #595
## 3.3.59(2024-12-30)
fix: Property "isH5" was accessed during render
## 3.3.58(2024-12-26)
fix: slider组件change事件传参
## 3.3.57(2024-12-23)
fix: slider组件change事件传参
feat: 更新u-picker组件增加当前选中class类名
## 3.3.56(2024-12-18)
feat: 在u-alert组件中添加关闭事件
## 3.3.55(2024-12-17)
add: swiper增加双向绑定
## 3.3.54(2024-12-11)
add: qrcode支持props控制是否开启点击预览
add: 新增cate-tab垂直分类组件
## 3.3.53(2024-12-10)
fix: 修复popup居中模式点击内容区域触发关闭
## 3.3.52(2024-12-09)
add: notice-bar支持justifyContent属性
## 3.3.51(2024-12-09)
add: radio增加label插槽
## 3.3.50(2024-12-05)
fix: 优化popup等对禁止背景滚动机制
add: slider在弹窗使用示例
fix: card组件类名问题
## 3.3.49(2024-12-02)
fix: 去除album多余的$u引用
fix: 优化图片组件兼容性
add: picker组件增加zIndex属性
add: text增加是否占满剩余空间属性
add: input颜色示例
## 3.3.48(2024-11-29)
add: 文本行数限制样式提高到10行
del: 去除不跨端的inputmode
## 3.3.47(2024-11-28)
fix: 时间选择器在hasInput模式下部分机型键盘弹出
## 3.3.46(2024-11-26)
fix: 修复text传递事件参数
## 3.3.45(2024-11-24)
add: navbar组件支持配置标题颜色
fix: 边框按钮警告类型下颜色变量使用错误
## 3.3.43(2024-11-18)
fix: 支持瀑布流组件v-model置为[]
add: 新增字符串路径访问工具方法getValueByPath
add: 新增float-button悬浮按钮组件
## 3.3.42(2024-11-15)
add: button组件支持stop参数阻止冒泡
## 3.3.41(2024-11-13)
fix: u-radio-group invalid import
improvement: 优化图片组件宽高及修复事件event传递
## 3.3.40(2024-11-11)
add: 组件radioGroup增加gap属性用于设置item间隔
fix: 修复H5全局导入
## 3.3.39(2024-11-04)
fix: 修复相册组件
## 3.3.38(2024-11-04)
fix: 修复视频预览报错 #510
add: album组件增加stop参数支持阻止事件冒泡
## 3.3.37(2024-10-21)
fix: 修复因为修改组件名称前缀,导致h5打包后$parent方法内找不到父组件的问题
fix: 修复datetime-picker选择2000年以前日期出错
## 3.3.36(2024-10-09)
fix: toast 自动关闭
feat: 增加微信小程序用户昵称审核完毕回调及修改 ts 定义文件
## 3.3.35(2024-10-08)
feat: modal和picker支持v-model:show双向绑定
feat: 支持checkbox使用slot自定义label后自带点击事件 #522
feat: swipe-action支持自动关闭特性及初始化打开状态
## 3.3.34(2024-09-23)
feat: 支持toast设置duration值为-1时不自动关闭
## 3.3.33(2024-09-18)
fix: 修复test.date('008')等验证结果不准确
## 3.3.32(2024-09-09)
fix: u-keyboard名称冲突warning
## 3.3.31(2024-08-31)
feat: qrcode初步支持nvue
## 3.3.30(2024-08-30)
fix: slider兼容step为字符串类型
## 3.3.29(2024-08-30)
fix: 修复tabs组件current参数为字符串处理逻辑
## 3.3.28(2024-08-26)
fix: list组件滑动偏移量不一样取绝对值导致iOS下拉偏移量计算错误
## 3.3.27(2024-08-22)
fix: 修复up-datetime-picker组件toolbarRightSlot定义缺失
fix: 修复FormItem的rules更新错误的问题
## 3.3.26(2024-08-22)
fix: 批量注册全局组件优化
## 3.3.25(2024-08-21)
fix: 修复slider在app-vue下样式问题
## 3.3.24(2024-08-19)
fix: 修复时间选择器hasInput模式小程序不生效
feat: 支持H5导入所有组件
## 3.3.23(2024-08-17)
feat: swipe-action增加closeAll方法
fix: 兼容tabs在某些场景下index小于0时自动设置为0
add: 通用mixin新增navTo页面跳转方法
## 3.3.21(2024-08-15)
improvement: 优化二维码组件loading及支持预览与长按事件 #351
fix: 修复swipe-action自动关闭其它功能及组件卸载自动关闭
## 3.3.20(2024-08-15)
refactor: props默认值文件移至组件文件夹内便于查找
## 3.3.19(2024-08-14)
fix: 修复2被rpx兼容处理只在数字值生效
add: 增加swiper自定义插槽示例
## 3.3.18(2024-08-13)
feat: 新增支持datetime-picker工具栏插槽及picker插槽支持修复
## 3.3.17(2024-08-12)
feat: swiper组件增加默认slot便于自定义
feat: grid新增间隔参数
feat: picker新增toolbar-right和toolbar-bottom插槽
## 3.3.16(2024-08-12)
fix: 解决swiper中title换行后多余的内容未被遮挡问题
fix: 修复迷你导航适配异形屏
## 3.3.15(2024-08-09)
fix: 修复默认单位设置为rpx时一些组件高度间距异常
fix: 修复日历在rpx单位下布局异常
feat: code-input支持App端展示输入光标
## 3.3.14(2024-08-09)
add: 增加box组件
add: 增加card卡片组件
## 3.3.13(2024-08-08)
feat: input支持调用原生组件的focus和blur方法
improvement: grid-item条件编译优化
add: 新增迷你导航组件
## 3.3.12(2024-08-06)
improvement: $u挂载时机调整便于打包分离chunk
fix: steps新增itemStyle属性名称冲突
## 3.3.11(2024-08-05)
feat: 新增支持upload组件的deletable/maxCount/accept变更监听 #333
feat: 新增支持tabs在swiper中使用
feat: 新增FormItem支持独立设置验证规则rules
fix: 修复index-list未设置$slots.header时索引高亮失效
## 3.3.10(2024-08-02)
fix: 修复index-list偶发的滑动最后一个索引报错top不存在
fix: 修复gird在QQ、抖音小程序下布局
feat: 优化step支持自定义样式prop
feat: action-sheet组件支持v-model:show双向绑定
fix: 小程序下steps和grid都统一采用grid布局
fix: 修复支付宝小程序下input类型为数字时双向绑定失效
feat : form 表单 validate 校验不通过后 error增加字段prop信息 #304
fix: form组件异步校异常验问题 #393
## 3.3.9(2024-08-01)
fix: 优化获取nvue元素
feat: modal新增contentTextAlign设置文案对齐方式
fix: 修复NVUE下tabbar文字不显示 #458
feat: loading-page增加zIndex属性
fix: 相册在宽度较小时换行问题
feat: album相册增加自适应自动换行模式
feat: album相册增加图片尺寸单位prop
fix: 修复calendar日历月份居中
## 3.3.8(2024-07-31)
feat: slider支持进度条任意位置触发按钮拖动
fix: 修复app-vue下modal标题不居中
fix: #459 TS setConfig 声明异常
feat: tabs组件增加longPress长按事件
feat: 新增showRight属性控制collapse右侧图标显隐
fix: 优化nvue下css警告
## 3.3.7(2024-07-29)
feat: 支持IndexList组件支持在弹窗等场景下使用及联动优化
feat: popup组件支持v-model:show双向绑定
feat: 优化tabs的current双向绑定
fix: checkbox独立使用时checked赋初始值可以,但是手动切换时值没有做双向绑定! #455
feat: slider组件支持区间双滑块
fix: toast 支持自定义图标?可传入了决对路径的 icon也没有用 #409
feat: form-item校验失败时 增加class方便自定义显示错误的展示方式 #394
fix: up-cell的required配置不生效 #395
fix: 横向滚动组件,微信小程序编译后会有警告 #415
fix: u-picker内部对默认值defaultIndex的监听 #425
feat: toast 组件支持遮掩层穿透 #417
fix: 兼容vue的slot编译bug #423
fix: upload 微信小程序 点击预览视频报错 #424
fix: u-number-box 组件修改【integer, decimalLength, min, max 】props时没有触发绑定值更新 #429
feat: Tabs组件能否支持自定义插槽 #439
feat: ActionSheet 可以配置最大高度吗, 我当做select使用了。 #445
fix: cursor-pointer优化
feat: 新版slider组件兼容NVUE改造
feat: 新增slider组件手动实现以支持样式自定义
perf:补充TS声明提示信息
修复:ActionSheet 操作菜单cancelText属性为空DOM节点还存在并且可以点击问题
fix: 去除预留的beforeDestroy兼容容易在某些sdk下不识别条件编译
## 3.3.6(2024-07-23)
feat: u-album组件添加radius,shape参数,定义参考当前u-image参数
fix: 修复了calendar组件title和日期title未垂直居中的问题
fix: update:modelValue缺失emit定义
## 3.3.5(2024-07-10)
picker组件支持hasInput模式
## 3.3.4(2024-07-07)
fix: input组件双向绑定问题 #419
lazy-load完善emit
优化通用小程序分享
## 3.3.2(2024-06-27)
fix: 在Nvue环境中编译,出现大量警告 #406
## 3.3.1(2024-06-27)
u-button组件报错,找不到button mixins #407
## 3.3.0(2024-06-27)
feat: checkbox支持label设置slot
feat: modal增加customClass
feat: navbar、popup、tabs、text支持customClass
fix: cell组建缺少flex布局
fix: 修复微信小程序真机调试时快速输入出现文本回退问题
feat: tag增加默认slot
公共mixin改造为按需导入语法
refactor: 组件props混入mixin改造为按需导入语法
fix: u-tabbar 安卓手机点击按钮变蓝问题 #396
feat: upload组建增加extension属性
fix: upload组件参数mode添加left
fix: 修复阴影在非nvue时白色背景色不显示
## 3.2.24(2024-06-11)
fix: 修复时间选择器confirm事件触发时机导致2次才会触发v-model更新
## 3.2.23(2024-05-30)
fix: #378 H5 u-input 在表单中初始值为空也会触发一次 formValidate(this,"change")事件导致进入页面直接校验了一次
fix: #373 搜索组件up-search的@clear事件无效
fix: #372 ActionSheet 组件的取消按钮触发区域太小
## 3.2.22(2024-05-13)
上传组件支持微信小程序预览视频
修复折叠面板右侧箭头不显示
修复uxp2px
## 3.2.21(2024-05-10)
fix: loading-icon修复flex布局
## 3.2.20(2024-05-10)
修复瀑布流大小写#355
## 3.2.19(2024-05-10)
去除意外的文件引入
## 3.2.18(2024-05-09)
fix: 349 popup 组件设置 zIndex 属性后,组件渲染异常#
feat: 搜索框增加adjustPosition属性
fix: #331增加u-action-sheet__cancel
优化mixin兼容性
feat: #326 up-list增加下拉刷新功能
fix: #319 优化up-tabs参数与定义匹配
fix: index-list组件微信小程序端使用自定义导航栏异常
fix: #285 pickerimmediateChange 写死为true
fix: #111 u-scroll-list组件,隐藏指示器后报错, 提示找不到ref
list增加微信小程序防抖配置
## 3.2.17(2024-05-08)
fix: 支付宝小程序二维码渲染
## 3.2.16(2024-05-06)
修复tabs中,当前激活样式的undefined bug
fix: #341u-code 倒计时没结束前退出,再次进入结束后退出界面,再次进入重新开始倒计时bug
受到uni-app内置text样式影响修复
## 3.2.15(2024-04-28)
优化时间选择器hasInput模式初始化值
## 3.2.14(2024-04-24)
去除pleaseSetTranspileDependencies
http采用useStore
## 3.2.13(2024-04-22)
修复modal标题样式
优化日期选择器hasInput模式宽度
## 3.2.12(2024-04-22)
修复color应用
## 3.2.11(2024-04-18)
修复import化带来的问题
## 3.2.10(2024-04-17)
完善input清空事件App端失效的兼容性
修复日历组件二次打开后当前月份显示不正确
## 3.2.9(2024-04-16)
组件内uni.$u用法改为import引入
规范化及兼容性增强
## 3.2.8(2024-04-15)
修复up-tag语法错
## 3.2.7(2024-04-15)
修复下拉菜单背景色在支付宝小程序无效
setConfig改为浅拷贝解决无法用import导入代替uni.$u.props设置
## 3.2.6(2024-04-14)
修复某些情况下滑动单元格默认右侧按钮是展开的问题
## 3.2.5(2024-04-13)
调整分段器尺寸及修复窗口大小改变时重新计算尺寸
多个组件支持cursor-pointer增强PC端体验
## 3.2.4(2024-04-12)
初步支持typescript
## 3.2.3(2024-04-12)
fix: 修复square属性在小程序下无效问题
fix:修复lastIndex异常导致的column异常问题
fix: alipayapp picker style
feat(button): 添加用户同意隐私协议事件回调
fix: input switch password
fix: 修复u-code组件keepRuning失效问题
feat: form-item添加labelPosition属性
新增dropdown组件
分段器支持内部current值
优化cell和action-sheet视觉大小
修复tabs文字换行
## 3.2.2(2024-04-11)
修复换行符问题
## 3.2.1(2024-04-11)
修复演示H5二维码
fix: #270 ReadMore 展开阅读更多内容变化兼容
fix: #238Calendar组件maxDate修改为不能小于minDate
checkbox支持独立使用
修复popup中在微信小程序中真机调试滚动失效
## 3.2.0(2024-04-10)
修复轮播图在nvue显示
修复疑似u-slider名称被占用导致slider在App下不显示
解决微信小程序提示 Some selectors are not allowed in component wxss
示例中u-前缀统一为up-
增加瀑布流与图片懒加载组件
fix: #308修复tag组件缺失iconColor参数
fix: #297使用grid布局解决目前编译为抖音小程序无法开启virtualHost
## 3.1.52(2024-04-07)
工具类方法调用import化改造
新增up-copy复制组件
## 3.1.51(2024-04-07)
优化时间选择器自带输入框格式化显示
防止按钮文字换行
修复订单列表模板滑动
增加u-qrcode二维码组件
## 3.1.49(2024-03-27)
日期时间组件支持自带输入框
fix: popup弹窗滚动穿透问题
fix: 修复小程序numberbox bug
## 3.1.48(2024-03-18)
fix:[plugin:uni:pre-css] Unbalanced delimiter found in string
## 3.1.47(2024-03-18)
fix: setConfig设置组件默认参数无效问题
fix: 修复自定义图标无效问题
feat: 增加u-form-item单独设置规则变量
fix:#293小程序是自定义导航栏的时候即传了customNavHeight的时候会出现跳转偏移的情况
## 3.1.46(2024-01-29)
beforeUnmount
## 3.1.45(2024-01-24)
fix: #262ext组件为超链接的情况下size属性不生效
fix: #263最新版本3.1.42中微信小程序u-swipe-action-item报错
fix: #224最新版本3.1.42中微信小程序u-swipe-action-item报错
fix: #263支持支付宝小程序
fix: #261u-input在直接修改v-model的绑定值时,每隔一次会无法出发change事件
优化折叠面板兼容微信小程序
## 3.1.42(2024-01-15)
修复u-number-box默认值0时在小程序不显示值
优化u-code的timer判断
优化支付宝小程序下textarea字数统计兼容
优化u-calendar
## 3.1.41(2023-11-18)
#215优化u-cell图标容器间距问题
## 3.1.40(2023-11-16)
修复u-slider双向绑定
## 3.1.39(2023-11-10)
修复头条小程序不支持env(safe-area-inset-bottom)
优化#201u-grid 指定列数导致闪烁
#193IndexList 索引列表 高度错误
其他优化
## 3.1.38(2023-10-08)
修复u-slider
## 3.1.37(2023-09-13)
完善emits定义及修复code-input双向数据绑定
## 3.1.36(2023-08-08)
修复富文本事件名称大小写
## 3.1.35(2023-08-02)
修复编译到支付宝小程序u-form报错
## 3.1.34(2023-07-27)
修复App打包uni.$u.mpMixin方式sdk暂时不支持导致报错
## 3.1.33(2023-07-13)
修复弹窗进入动画、模板页面样式等
## 3.1.31(2023-07-11)
修复dayjs引用
## 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)初步发布

@ -0,0 +1,85 @@
<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';
import { mixin } from '../../libs/mixin/mixin';
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)
},
/**
* 校验全部数据
* @param {Object} options
* @param {Boolean} options.showErrorMsg -是否显示校验信息
*/
validate(options) {
/**
* 在微信小程序中通过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(options)
},
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>

@ -0,0 +1,50 @@
<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';
import { mixin } from '../../libs/mixin/mixin';
export default {
name: 'u--image',
mixins: [mpMixin, props, mixin],
components: {
uvImage
},
emits: ['click', 'error', 'load']
}
</script>

@ -0,0 +1,74 @@
<template>
<uvInput
<!-- #ifdef VUE2 -->
:value="value"
@input="e => $emit('input', e)"
<!-- #endif -->
<!-- #ifdef VUE3 -->
:modelValue="modelValue"
@update:modelValue="e => $emit('update:modelValue', e)"
<!-- #endif -->
: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"
>
<!-- #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';
import { mixin } from '../../libs/mixin/mixin';
export default {
name: 'u--input',
mixins: [mpMixin, props, mixin],
components: {
uvInput
},
}
</script>

@ -0,0 +1,45 @@
<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>

@ -0,0 +1,47 @@
<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';
import { mixin } from '../../libs/mixin/mixin';
export default {
name: 'u--textarea',
mixins: [mpMixin, props, mixin],
components: {
uvTextarea
},
}
</script>

@ -0,0 +1,109 @@
<template>
<view class="u-action-sheet-data">
<view class="u-action-sheet-data__trigger">
<slot name="trigger"></slot>
<up-input
v-if="!$slots['trigger']"
:modelValue="current"
disabled
disabledColor="#ffffff"
:placeholder="title"
border="none"
></up-input>
<view @click="show = true"
class="u-action-sheet-data__trigger__cover"></view>
</view>
<up-action-sheet
:show="show"
:actions="options"
:title="title"
safeAreaInsetBottom
:description="description"
@close="show = false"
@select="select"
>
</up-action-sheet>
</view>
</template>
<script>
export default {
props: {
modelValue: {
type: [String, Number],
default: ''
},
title: {
type: String,
default: ''
},
description: {
type: String,
default: ''
},
options: {
type: Array,
default: () => {
return []
}
},
valueKey: {
type: String,
default: 'value'
},
labelKey: {
type: String,
default: 'name'
}
},
data() {
return {
show: false,
current: '',
}
},
created() {
if (this.modelValue) {
this.options.forEach((ele) => {
if (ele[this.valueKey] == this.modelValue) {
this.current = ele[this.labelKey]
}
})
}
},
emits: ['update:modelValue'],
watch: {
modelValue() {
this.options.forEach((ele) => {
if (ele[this.valueKey] == this.modelValue) {
this.current = ele[this.labelKey]
}
})
}
},
methods: {
hideKeyboard() {
uni.hideKeyboard()
},
select(e) {
this.$emit('update:modelValue', e[this.valueKey])
this.current = e[this.labelKey]
},
}
}
</script>
<style lang="scss" scoped>
.u-action-sheet-data {
&__trigger {
position: relative;
&__cover {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
}
}
</style>

@ -0,0 +1,26 @@
/*
* @Author : LQ
* @Description :
* @version : 1.0
* @Date : 2021-08-20 16:44:21
* @LastAuthor : LQ
* @lastTime : 2021-08-20 16:44:35
* @FilePath : /u-view2.0/uview-ui/libs/config/props/actionSheet.js
*/
export default {
// action-sheet组件
actionSheet: {
show: false,
title: '',
description: '',
actions: [],
index: '',
cancelText: '',
closeOnClickAction: true,
safeAreaInsetBottom: true,
openType: '',
closeOnClickOverlay: true,
round: 0,
wrapMaxHeight: '600px'
}
}

@ -0,0 +1,62 @@
import { defineMixin } from '../../libs/vue'
import defProps from '../../libs/config/props.js'
export const props = defineMixin({
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
},
// 选项区域最大高度
wrapMaxHeight: {
type: [String],
default: () => defProps.actionSheet.wrapMaxHeight
},
}
})

@ -0,0 +1,282 @@
<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>
<scroll-view scroll-y class="u-action-sheet__item-wrap" :style="{maxHeight: wrapMaxHeight}">
<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>
</scroll-view>
</slot>
<u-gap
bgColor="#eaeaec"
height="6"
v-if="cancelText"
></u-gap>
<view class="u-action-sheet__item-wrap__item u-action-sheet__cancel"
hover-class="u-action-sheet--hover" @tap="cancel" v-if="cancelText">
<text
@touchmove.stop.prevent
:hover-stay-time="150"
class="u-action-sheet__cancel-text"
>{{cancelText}}</text>
</view>
</view>
</u-popup>
</template>
<script>
import { openType } from '../../libs/mixin/openType'
import { buttonMixin } from '../../libs/mixin/button'
import { props } from './props';
import { mpMixin } from '../../libs/mixin/mpMixin';
import { mixin } from '../../libs/mixin/mixin';
import { addUnit } from '../../libs/function/index';
/**
* ActionSheet 操作菜单
* @description 本组件用于从底部弹出一个操作菜单供用户选择并返回结果本组件功能类似于uni的uni.showActionSheetAPI配置更加灵活所有平台都表现一致
* @tutorial https://ijry.github.io/uview-plus/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",
// propsmethodsmixin
mixins: [openType, buttonMixin, 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 = addUnit(this.actions[index].fontSize)
//
if (this.actions[index].disabled) style.color = '#c0c4cc'
return style;
}
},
},
emits: ["close", "select", "update:show"],
methods: {
closeHandler() {
// close
if(this.closeOnClickOverlay) {
this.$emit('update:show', false)
this.$emit('close')
}
},
//
cancel() {
this.$emit('update:show', false)
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('update:show', false)
this.$emit('close')
}
}
},
}
}
</script>
<style lang="scss" scoped>
$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:17px !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>

@ -0,0 +1,28 @@
/*
* @Author : LQ
* @Description :
* @version : 1.0
* @Date : 2021-08-20 16:44:21
* @LastAuthor : LQ
* @lastTime : 2021-08-20 16:47:24
* @FilePath : /u-view2.0/uview-ui/libs/config/props/album.js
*/
export default {
// album 组件
album: {
urls: [],
keyName: '',
singleSize: 180,
multipleSize: 70,
space: 6,
singleMode: 'scaleToFill',
multipleMode: 'aspectFill',
maxCount: 9,
previewFullImage: true,
rowCount: 3,
showMore: true,
autoWrap: false,
unit: 'px',
stop: true,
}
}

@ -0,0 +1,86 @@
import { defineMixin } from '../../libs/vue'
import defProps from '../../libs/config/props.js'
export const props = defineMixin({
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
},
// 图片形状,circle-圆形,square-方形
shape: {
type: String,
default: () => defProps.image.shape
},
// 圆角,单位任意
radius: {
type: [String, Number],
default: () => defProps.image.radius
},
// 自适应换行
autoWrap: {
type: Boolean,
default: () => defProps.album.autoWrap
},
// 单位
unit: {
type: [String],
default: () => defProps.album.unit
},
// 阻止点击冒泡
stop: {
type: Boolean,
default: () => defProps.album.stop
}
}
})

@ -0,0 +1,300 @@
<template>
<view class="u-album">
<view
class="u-album__row"
ref="u-album__row"
v-for="(arr, index) in showUrls"
:forComputedUse="albumWidth"
:key="index"
:style="{flexWrap: autoWrap ? 'wrap' : 'nowrap'}"
>
<view
class="u-album__row__wrapper"
v-for="(item, index1) in arr"
:key="index1"
:style="[imageStyle(index + 1, index1 + 1)]"
@tap="onPreviewTap($event, getSrc(item))"
>
<image
:src="getSrc(item)"
:mode="
urls.length === 1
? imageHeight > 0
? singleMode
: 'widthFix'
: multipleMode
"
:style="[
{
width: imageWidth,
height: imageHeight,
borderRadius: shape == 'circle' ? '10000px' : addUnit(radius)
}
]"
></image>
<view
v-if="
showMore &&
urls.length > rowCount * showUrls.length &&
index === showUrls.length - 1 &&
index1 === showUrls[showUrls.length - 1].length - 1
"
class="u-album__row__wrapper__text"
:style="{
borderRadius: shape == 'circle' ? '50%' : addUnit(radius),
}"
>
<up-text
:text="`+${urls.length - maxCount}`"
color="#fff"
:size="multipleSize * 0.3"
align="center"
customStyle="justify-content: center"
></up-text>
</view>
</view>
</view>
</view>
</template>
<script>
import { props } from './props';
import { mpMixin } from '../../libs/mixin/mpMixin';
import { mixin } from '../../libs/mixin/mixin';
import { addUnit, sleep } from '../../libs/function/index';
import test from '../../libs/function/test';
// #ifdef APP-NVUE
// weexKPIdom
const dom = uni.requireNativePlugin('dom')
// #endif
/**
* Album 相册
* @description 本组件提供一个类似相册的功能让开发者开发起来更加得心应手减少重复的模板代码
* @tutorial https://ijry.github.io/uview-plus/components/album.html
*
* @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
* @property {String | Number} singleSize 单图时图片长边的长度 默认 180
* @property {String | Number} multipleSize 多图时图片边长 默认 70
* @property {String | Number} space 多图时图片水平和垂直之间的间隔 默认 6
* @property {String} singleMode 单图时图片缩放裁剪的模式 默认 'scaleToFill'
* @property {String} multipleMode 多图时图片缩放裁剪的模式 默认 'aspectFill'
* @property {String | Number} maxCount 取消按钮的提示文字 默认 9
* @property {Boolean} previewFullImage 是否可以预览图片 默认 true
* @property {String | Number} rowCount 每行展示图片数量如设置singleSize和multipleSize将会无效 默认 3
* @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 默认 true
* @property {String} shape 图片形状circle-圆形square-方形 默认 'square'
* @property {String | Number} radius 圆角值单位任意如果为数值则为px单位 默认 0
* @property {Boolean} autoWrap 自适应换行模式不受rowCount限制图片会自动换行 默认 false
* @property {String} unit 图片单位 默认 px
* @event {Function} albumWidth 某些特殊的情况下需要让文字与相册的宽度相等这里事件的形式对外发送 回调参数 width
* @example <u-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></u-album>
*/
export default {
name: 'u-album',
mixins: [mpMixin, mixin, props],
data() {
return {
//
singleWidth: 0,
//
singleHeight: 0,
//
singlePercent: 0.6
}
},
watch: {
urls: {
immediate: true,
handler(newVal) {
if (newVal.length === 1) {
this.getImageRect()
}
}
}
},
emits: ["albumWidth"],
computed: {
imageStyle() {
return (index1, index2) => {
const { space, rowCount, multipleSize, urls } = this,
rowLen = this.showUrls.length,
allLen = this.urls.length
const style = {
marginRight: addUnit(space),
marginBottom: addUnit(space)
}
//
if (index1 === rowLen && !this.autoWrap) style.marginBottom = 0
//
if (!this.autoWrap) {
if (
index2 === rowCount ||
(index1 === rowLen &&
index2 === this.showUrls[index1 - 1].length)
)
style.marginRight = 0
}
return style
}
},
//
showUrls() {
if (this.autoWrap) {
return [ this.urls.slice(0, this.maxCount) ];
} else {
const arr = []
this.urls.map((item, index) => {
//
if (index + 1 <= this.maxCount) {
//
const itemIndex = Math.floor(index / this.rowCount)
//
if (!arr[itemIndex]) {
arr[itemIndex] = []
}
arr[itemIndex].push(item)
}
})
return arr
}
},
imageWidth() {
return addUnit(
this.urls.length === 1 ? this.singleWidth : this.multipleSize, this.unit
)
},
imageHeight() {
return addUnit(
this.urls.length === 1 ? this.singleHeight : this.multipleSize, this.unit
)
},
// computedurls
//
albumWidth() {
let width = 0
if (this.urls.length === 1) {
width = this.singleWidth
} else {
width =
this.showUrls[0].length * this.multipleSize +
this.space * (this.showUrls[0].length - 1)
}
this.$emit('albumWidth', width)
return width
}
},
emits: ['preview', 'albumWidth'],
methods: {
addUnit,
//
onPreviewTap(e, url) {
const urls = this.urls.map((item) => {
return this.getSrc(item)
})
if (this.previewFullImage) {
uni.previewImage({
current: url,
urls
})
//
this.stop && this.preventEvent(e)
} else {
this.$emit('preview', {
urls,
currentIndex: urls.indexOf(url)
})
}
},
//
getSrc(item) {
return test.object(item)
? (this.keyName && item[this.keyName]) || item.src
: item
},
//
// download
// (singlePercent)
getImageRect() {
const src = this.getSrc(this.urls[0])
uni.getImageInfo({
src,
success: (res) => {
let singleSize = this.singleSize;
//
let unit = '';
if (Number.isNaN(Number(this.singleSize))) {
//
unit = this.singleSize.replace(/\d+/g, ''); //
singleSize = Number(this.singleSize.replace(/\D+/g, ''), 10); //
}
//
const isHorizotal = res.width >= res.height
this.singleWidth = isHorizotal
? singleSize
: (res.width / res.height) * singleSize
this.singleHeight = !isHorizotal
? singleSize
: (res.height / res.width) * this.singleWidth
//
if(unit != null && unit !== ''){
this.singleWidth = this.singleWidth + unit
this.singleHeight = this.singleHeight + unit
}
},
fail: () => {
this.getComponentWidth()
}
})
},
//
async getComponentWidth() {
// dom
await sleep(30)
// #ifndef APP-NVUE
this.$uGetRect('.u-album__row').then((size) => {
this.singleWidth = size.width * this.singlePercent
})
// #endif
// #ifdef APP-NVUE
// ref="u-album__row"forthis.$refs['u-album__row']
const ref = this.$refs['u-album__row'][0]
ref &&
dom.getComponentRect(ref, (res) => {
this.singleWidth = res.size.width * this.singlePercent
})
// #endif
}
}
}
</script>
<style lang="scss" scoped>
.u-album {
@include flex(column);
&__row {
@include flex(row);
&__wrapper {
position: relative;
&__text {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
@include flex(row);
justify-content: center;
align-items: center;
}
}
}
}
</style>

@ -0,0 +1,22 @@
/*
* @Author : LQ
* @Description :
* @version : 1.0
* @Date : 2021-08-20 16:44:21
* @LastAuthor : LQ
* @lastTime : 2021-08-20 16:48:53
* @FilePath : /u-view2.0/uview-ui/libs/config/props/alert.js
*/
export default {
// alert警告组件
alert: {
title: '',
type: 'warning',
description: '',
closable: false,
showIcon: false,
effect: 'light',
center: false,
fontSize: 14
}
}

@ -0,0 +1,46 @@
import { defineMixin } from '../../libs/vue'
import defProps from '../../libs/config/props.js'
export const props = defineMixin({
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
}
}
})

@ -0,0 +1,250 @@
<template>
<u-transition
mode="fade"
:show="show"
>
<view
class="u-alert"
:class="[`u-alert--${type}--${effect}`]"
@tap.stop="clickHandler"
:style="[addStyle(customStyle)]"
>
<view
class="u-alert__icon"
v-if="showIcon"
>
<u-icon
:name="iconName"
size="18"
:color="iconColor"
></u-icon>
</view>
<view
class="u-alert__content"
:style="[{
paddingRight: closable ? '20px' : 0
}]"
>
<text
class="u-alert__content__title"
v-if="title"
:style="[{
fontSize: addUnit(fontSize),
textAlign: center ? 'center' : 'left'
}]"
:class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
>{{ title }}</text>
<text
class="u-alert__content__desc"
v-if="description"
:style="[{
fontSize: addUnit(fontSize),
textAlign: center ? 'center' : 'left'
}]"
:class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
>{{ description }}</text>
</view>
<view
class="u-alert__close"
v-if="closable"
@tap.stop="closeHandler"
>
<u-icon
name="close"
:color="iconColor"
size="15"
></u-icon>
</view>
</view>
</u-transition>
</template>
<script>
import { props } from './props';
import { mpMixin } from '../../libs/mixin/mpMixin';
import { mixin } from '../../libs/mixin/mixin';
import { addUnit, addStyle } from '../../libs/function/index';
/**
* Alert 警告提示
* @description 警告提示展现需要关注的信息
* @tutorial https://ijry.github.io/uview-plus/components/alertTips.html
*
* @property {String} title 显示的文字
* @property {String} type 使用预设的颜色 默认 'warning'
* @property {String} description 辅助性文字颜色比title浅一点字号也小一点可选
* @property {Boolean} closable 关闭按钮(默认为叉号icon图标) 默认 false
* @property {Boolean} showIcon 是否显示左边的辅助图标 默认 false
* @property {String} effect 多图时图片缩放裁剪的模式 默认 'light'
* @property {Boolean} center 文字是否居中 默认 false
* @property {String | Number} fontSize 字体大小 默认 14
* @property {Object} customStyle 定义需要用到的外部样式
* @event {Function} click 点击组件时触发
* @event {Function} close 点击关闭按钮时触发
* @example <u-alert :title="title" type = "warning" :closable="closable" :description = "description"></u-alert>
*/
export default {
name: 'u-alert',
mixins: [mpMixin, mixin, props],
data() {
return {
show: true
}
},
computed: {
iconColor() {
return this.effect === 'light' ? this.type : '#fff'
},
//
iconName() {
switch (this.type) {
case 'success':
return 'checkmark-circle-fill';
break;
case 'error':
return 'close-circle-fill';
break;
case 'warning':
return 'error-circle-fill';
break;
case 'info':
return 'info-circle-fill';
break;
case 'primary':
return 'more-circle-fill';
break;
default:
return 'error-circle-fill';
}
}
},
emits: ["click","close"],
methods: {
addUnit,
addStyle,
//
clickHandler() {
this.$emit('click')
},
//
closeHandler() {
this.show = false
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
.u-alert {
position: relative;
background-color: $u-primary;
padding: 8px 10px;
@include flex(row);
align-items: center;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
&--primary--dark {
background-color: $u-primary;
}
&--primary--light {
background-color: #ecf5ff;
}
&--error--dark {
background-color: $u-error;
}
&--error--light {
background-color: #FEF0F0;
}
&--success--dark {
background-color: $u-success;
}
&--success--light {
background-color: #f5fff0;
}
&--warning--dark {
background-color: $u-warning;
}
&--warning--light {
background-color: #FDF6EC;
}
&--info--dark {
background-color: $u-info;
}
&--info--light {
background-color: #f4f4f5;
}
&__icon {
margin-right: 5px;
}
&__content {
@include flex(column);
flex: 1;
&__title {
color: $u-main-color;
font-size: 14px;
font-weight: bold;
color: #fff;
margin-bottom: 2px;
}
&__desc {
color: $u-main-color;
font-size: 14px;
flex-wrap: wrap;
color: #fff;
}
}
&__title--dark,
&__desc--dark {
color: #FFFFFF;
}
&__text--primary--light,
&__text--primary--light {
color: $u-primary;
}
&__text--success--light,
&__text--success--light {
color: $u-success;
}
&__text--warning--light,
&__text--warning--light {
color: $u-warning;
}
&__text--error--light,
&__text--error--light {
color: $u-error;
}
&__text--info--light,
&__text--info--light {
color: $u-info;
}
&__close {
position: absolute;
top: 11px;
right: 10px;
}
}
</style>

@ -0,0 +1,23 @@
/*
* @Author : LQ
* @Description :
* @version : 1.0
* @Date : 2021-08-20 16:44:21
* @LastAuthor : LQ
* @lastTime : 2021-08-20 16:49:55
* @FilePath : /u-view2.0/uview-ui/libs/config/props/avatarGroup.js
*/
export default {
// avatarGroup 组件
avatarGroup: {
urls: [],
maxCount: 5,
shape: 'circle',
mode: 'scaleToFill',
showMore: true,
size: 40,
keyName: '',
gap: 0.5,
extraValue: 0
}
}

@ -0,0 +1,54 @@
import { defineMixin } from '../../libs/vue'
import defProps from '../../libs/config/props.js'
export const props = defineMixin({
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
}
}
})

@ -0,0 +1,109 @@
<template>
<view class="u-avatar-group">
<view
class="u-avatar-group__item"
v-for="(item, index) in showUrl"
:key="index"
:style="{
marginLeft: index === 0 ? 0 : addUnit(-size * gap)
}"
>
<u-avatar
:size="size"
:shape="shape"
:mode="mode"
:src="testObject(item) ? keyName && item[keyName] || item.url : item"
></u-avatar>
<view
class="u-avatar-group__item__show-more"
v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)"
@tap="clickHandler"
>
<up-text
color="#ffffff"
:size="size * 0.4"
:text="`+${extraValue || urls.length - showUrl.length}`"
align="center"
customStyle="justify-content: center"
></up-text>
</view>
</view>
</view>
</template>
<script>
import { props } from './props';
import { mpMixin } from '../../libs/mixin/mpMixin';
import { mixin } from '../../libs/mixin/mixin';
import { addUnit } from '../../libs/function/index';
import test from '../../libs/function/test';
/**
* AvatarGroup 头像组
* @description 本组件一般用于展示头像的地方如个人中心或者评论列表页的用户头像展示等场所
* @tutorial https://ijry.github.io/uview-plus/components/avatar.html
*
* @property {Array} urls 头像图片组 默认 []
* @property {String | Number} maxCount 最多展示的头像数量 默认 5
* @property {String} shape 头像形状 'circle' (默认) | 'square'
* @property {String} mode 图片裁剪模式默认 'scaleToFill'
* @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 默认 true
* @property {String | Number} size 头像大小 默认 40
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
* @property {String | Number} gap 头像之间的遮挡比例0.4代表遮挡40% 默认 0.5
* @property {String | Number} extraValue 需额外显示的值
* @event {Function} showMore 头像组更多点击
* @example <u-avatar-group:urls="urls" size="35" gap="0.4" ></u-avatar-group:urls=>
*/
export default {
name: 'u-avatar-group',
mixins: [mpMixin, mixin, props],
data() {
return {
}
},
computed: {
showUrl() {
return this.urls.slice(0, this.maxCount)
}
},
emits: ["showMore"],
methods: {
addUnit,
testObject: test.object,
clickHandler() {
this.$emit('showMore')
}
},
}
</script>
<style lang="scss" scoped>
.u-avatar-group {
@include flex;
&__item {
margin-left: -10px;
position: relative;
&--no-indent {
// 使:first-childnvue
margin-left: 0;
}
&__show-more {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.3);
@include flex;
align-items: center;
justify-content: center;
border-radius: 100px;
}
}
}
</style>

@ -0,0 +1,28 @@
/*
* @Author : LQ
* @Description :
* @version : 1.0
* @Date : 2021-08-20 16:44:21
* @LastAuthor : LQ
* @lastTime : 2021-08-20 16:49:22
* @FilePath : /u-view2.0/uview-ui/libs/config/props/avatar.js
*/
export default {
// avatar 组件
avatar: {
src: '',
shape: 'circle',
size: 40,
mode: 'scaleToFill',
text: '',
bgColor: '#c0c4cc',
color: '#ffffff',
fontSize: 18,
icon: '',
mpAvatar: false,
randomBgColor: false,
defaultUrl: '',
colorIndex: '',
name: ''
}
}

@ -0,0 +1,81 @@
import { defineMixin } from '../../libs/vue'
import defProps from '../../libs/config/props.js'
import test from '../../libs/function/test';
export const props = defineMixin({
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 test.range(n, [0, 19]) || n === ''
},
default: () => defProps.avatar.colorIndex
},
// 组件标识符
name: {
type: String,
default: () => defProps.avatar.name
}
}
})

@ -0,0 +1,179 @@
<template>
<view
class="u-avatar"
:class="[`u-avatar--${shape}`]"
:style="[{
backgroundColor: (text || icon) ? (randomBgColor ? colors[colorIndex !== '' ? colorIndex : random(0, 19)] : bgColor) : 'transparent',
width: addUnit(size),
height: addUnit(size),
}, addStyle(customStyle)]"
@tap="clickHandler"
>
<slot>
<!-- #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU -->
<open-data
v-if="mpAvatar && allowMp"
type="userAvatarUrl"
:style="[{
width: addUnit(size),
height: addUnit(size)
}]"
/>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU -->
<template v-if="mpAvatar && allowMp"></template>
<!-- #endif -->
<u-icon
v-else-if="icon"
:name="icon"
:size="fontSize"
:color="color"
></u-icon>
<up-text
v-else-if="text"
:text="text"
:size="fontSize"
:color="color"
align="center"
customStyle="justify-content: center"
></up-text>
<image
class="u-avatar__image"
v-else
:class="[`u-avatar__image--${shape}`]"
:src="avatarUrl || defaultUrl"
:mode="mode"
@error="errorHandler"
:style="[{
width: addUnit(size),
height: addUnit(size)
}]"
></image>
</slot>
</view>
</template>
<script>
import { props } from './props';
import { mpMixin } from '../../libs/mixin/mpMixin';
import { mixin } from '../../libs/mixin/mixin';
import { addStyle, addUnit, random } from '../../libs/function/index';
const base64Avatar =
"data:image/jpg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjREMEQwRkY0RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjREMEQwRkY1RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NEQwRDBGRjJGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NEQwRDBGRjNGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwfHx8fHx8fHx8fAQcHBw0MDRgQEBgaFREVGh8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx//wAARCADIAMgDAREAAhEBAxEB/8QAcQABAQEAAwEBAAAAAAAAAAAAAAUEAQMGAgcBAQAAAAAAAAAAAAAAAAAAAAAQAAIBAwICBgkDBQAAAAAAAAABAhEDBCEFMVFBYXGREiKBscHRMkJSEyOh4XLxYjNDFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbHFyZ/Dam+yLA+Z2L0Pjtyj2poD4AAAAAAAAAAAAAAAAAAAAAAAAKWFs9y6lcvvwQeqj8z9wFaziY1n/HbUX9XF97A7QAGXI23EvJ1goyfzR0YEfN269jeZ+a03pNe0DIAAAAAAAAAAAAAAAAAAAACvtO3RcVkXlWutuL9YFYAAAAAOJRjKLjJVi9GmB5/csH/mu1h/in8PU+QGMAAAAAAAAAAAAAAAAAAaMDG/6MmMH8C80+xAelSSVFolwQAAAAAAAHVlWI37ErUulaPk+hgeYnCUJuElSUXRrrQHAAAAAAAAAAAAAAAAABa2Oz4bM7r4zdF2ICmAAAAAAAAAg7zZ8GX41wuJP0rRgYAAAAAAAAAAAAAAAAAD0m2R8ODaXU33tsDSAAAAAAAAAlb9HyWZcnJd9PcBHAAAAAAAAAAAAAAAAAPS7e64Vn+KA0AAAAAAAAAJm+v8Ftf3ewCKAAAAAAAAAAAAAAAAAX9muqeGo9NttP06+0DcAAAAAAAAAjb7dTu2ra+VOT9P8AQCWAAAAAAAAAAAAAAAAAUNmyPt5Ltv4bui/kuAF0AAAAAAADiUlGLlJ0SVW+oDzOXfd/Ind6JPRdS0QHSAAAAAAAAAAAAAAAAAE2nVaNcGB6Lbs6OTao9LsF51z60BrAAAAAABJ3jOVHjW3r/sa9QEgAAAAAAAAAAAAAAAAAAAPu1duWriuW34ZR4MC9hbnZyEoy8l36XwfYBsAAADaSq9EuLAlZ+7xSdrGdW9Hc5dgEdtt1erfFgAAAAAAAAAAAAAAAAADVjbblX6NR8MH80tEBRs7HYivyzlN8lovaBPzduvY0m6eK10TXtAyAarO55lpJK54orolr+4GqO/Xaea1FvqbXvA+Z77kNeW3GPbV+4DJfzcm/pcm3H6Vou5AdAFLC2ed2Pjv1txa8sV8T6wOL+yZEKu1JXFy4MDBOE4ScZxcZLinoB8gAAAAAAAAAAAB242LeyJ+C3GvN9C7QLmJtePYpKS+5c+p8F2IDYAANJqj1T4oCfk7Nj3G5Wn9qXJax7gJ93Z82D8sVNc4v30A6Xg5i42Z+iLfqARwcyT0sz9MWvWBps7LlTf5Grce9/oBTxdtxseklHxT+uWr9AGoAB138ezfj4bsFJdD6V2MCPm7RdtJzs1uW1xXzL3gTgAAAAAAAAADRhYc8q74I6RWs5ckB6GxYtWLat21SK731sDsAAAAAAAAAAAAAAAASt021NO/YjrxuQXT1oCOAAAAAAABzGLlJRSq26JAelwsWONYjbXxcZvmwO8AAAAAAAAAAAAAAAAAAef3TEWPkVivx3NY9T6UBiAAAAAABo2+VmGXblddIJ8eivRUD0oAAAAAAAAAAAAAAAAAAAYt4tKeFKVNYNSXfRgefAAAAAAAAr7VuSSWPedKaW5v1MCsAAAAAAAAAAAAAAAAAAIe6bj96Ts2n+JPzSXzP3ATgAAAAAAAAFbbt1UUrOQ9FpC4/UwK6aaqtU+DAAAAAAAAAAAAAAA4lKMIuUmoxWrb4ARNx3R3q2rLpa4Sl0y/YCcAAAAAAAAAAANmFud7G8r89r6X0dgFvGzLGRGtuWvTF6NAdwAAAAAAAAAAAy5W442PVN+K59EePp5ARMvOv5MvO6QXCC4AZwAAAAAAAAAAAAAcxlKLUotprg1owN+PvORborq+7Hnwl3gUbO74VzRydt8pKn68ANcJwmqwkpLmnUDkAAAAfNy9atqtyagut0AxXt5xIV8Fbj6lRd7Am5G65V6qUvtwfyx94GMAAAAAAAAAAAAAAAAAAAOU2nVOj5gdsc3LiqRvTpyqwOxbnnrhdfpSfrQB7pnv/AGvuS9gHXPMy5/Fem1yq0v0A6W29XqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z";
/**
* Avatar 头像
* @description 本组件一般用于展示头像的地方如个人中心或者评论列表页的用户头像展示等场所
* @tutorial https://ijry.github.io/uview-plus/components/avatar.html
*
* @property {String} src 头像路径如加载失败将会显示默认头像(不能为相对路径)
* @property {String} shape 头像形状 circle (默认) | square
* @property {String | Number} size 头像尺寸可以为指定字符串(large, default, mini)或者数值 默认 40
* @property {String} mode 头像图片的裁剪类型与uni的image组件的mode参数一致如效果达不到需求可尝试传widthFix值 默认 'scaleToFill'
* @property {String} text 用文字替代图片级别优先于src
* @property {String} bgColor 背景颜色一般显示文字时用 默认 '#c0c4cc'
* @property {String} color 文字颜色 默认 '#ffffff'
* @property {String | Number} fontSize 文字大小 默认 18
* @property {String} icon 显示的图标
* @property {Boolean} mpAvatar 显示小程序头像只对百度微信QQ小程序有效 默认 false
* @property {Boolean} randomBgColor 是否使用随机背景色 默认 false
* @property {String} defaultUrl 加载失败的默认头像(组件有内置默认图片)
* @property {String | Number} colorIndex 如果配置了randomBgColor为true且配置了此值则从默认的背景色数组中取出对应索引的颜色值取值0-19之间
* @property {String} name 组件标识符 默认 'level'
* @property {Object} customStyle 定义需要用到的外部样式
*
* @event {Function} click 点击组件时触发 index: 用户传递的标识符
* @example <u-avatar :src="src" mode="square"></u-avatar>
*/
export default {
name: 'u-avatar',
mixins: [mpMixin, mixin, props],
data() {
return {
// randomBgColortrue
colors: ['#ffb34b', '#f2bba9', '#f7a196', '#f18080', '#88a867', '#bfbf39', '#89c152', '#94d554', '#f19ec2',
'#afaae4', '#e1b0df', '#c38cc1', '#72dcdc', '#9acdcb', '#77b1cc', '#448aca', '#86cefa', '#98d1ee',
'#73d1f1',
'#80a7dc'
],
avatarUrl: this.src,
allowMp: false
}
},
watch: {
// srcavatarUrlsrc
// props
src: {
immediate: true,
handler(newVal) {
this.avatarUrl = newVal
// srcerrorsrc''
if(!newVal) {
this.errorHandler()
}
}
}
},
computed: {
imageStyle() {
const style = {}
return style
}
},
created() {
this.init()
},
emits: ["click"],
methods: {
addStyle,
addUnit,
random,
init() {
// open-data
// uni.getUserInfo()
//
// #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU
this.allowMp = true
// #endif
},
// name"/"
isImg() {
return this.src.indexOf('/') !== -1
},
//
errorHandler() {
this.avatarUrl = this.defaultUrl || base64Avatar
},
clickHandler(e) {
this.$emit('click', this.name, e)
}
}
}
</script>
<style lang="scss" scoped>
.u-avatar {
@include flex;
align-items: center;
justify-content: center;
&--circle {
border-radius: 100px;
}
&--square {
border-radius: 4px;
}
&__image {
&--circle {
border-radius: 100px;
overflow: hidden;
}
&--square {
border-radius: 4px;
}
}
}
</style>

@ -0,0 +1,27 @@
/*
* @Author : LQ
* @Description :
* @version : 1.0
* @Date : 2021-08-20 16:44:21
* @LastAuthor : LQ
* @lastTime : 2021-08-20 16:50:18
* @FilePath : /u-view2.0/uview-ui/libs/config/props/backtop.js
*/
export default {
// backtop组件
backtop: {
mode: 'circle',
icon: 'arrow-upward',
text: '',
duration: 100,
scrollTop: 0,
top: 400,
bottom: 100,
right: 20,
zIndex: 9,
iconStyle: {
color: '#909399',
fontSize: '19px'
}
}
}

@ -0,0 +1,56 @@
import { defineMixin } from '../../libs/vue'
import defProps from '../../libs/config/props.js'
export const props = defineMixin({
props: {
// 返回顶部的形状,circle-圆形,square-方形
mode: {
type: String,
default: () => defProps.backtop.mode
},
// 自定义图标
icon: {
type: String,
default: () => defProps.backtop.icon
},
// 提示文字
text: {
type: String,
default: () => defProps.backtop.text
},
// 返回顶部滚动时间
duration: {
type: [String, Number],
default: () => defProps.backtop.duration
},
// 滚动距离
scrollTop: {
type: [String, Number],
default: () => defProps.backtop.scrollTop
},
// 距离顶部多少距离显示,单位px
top: {
type: [String, Number],
default: () => defProps.backtop.top
},
// 返回顶部按钮到底部的距离,单位px
bottom: {
type: [String, Number],
default: () => defProps.backtop.bottom
},
// 返回顶部按钮到右边的距离,单位px
right: {
type: [String, Number],
default: () => defProps.backtop.right
},
// 层级
zIndex: {
type: [String, Number],
default: () => defProps.backtop.zIndex
},
// 图标的样式,对象形式
iconStyle: {
type: Object,
default: () => defProps.backtop.iconStyle
}
}
})

@ -0,0 +1,132 @@
<template>
<u-transition
mode="fade"
:customStyle="backTopStyle"
:show="show"
>
<view
class="u-back-top"
:style="[contentStyle]"
v-if="!$slots.default && !$slots.$default"
@click="backToTop"
>
<u-icon
:name="icon"
:custom-style="iconStyle"
></u-icon>
<text
v-if="text"
class="u-back-top__text"
>{{text}}</text>
</view>
<slot v-else />
</u-transition>
</template>
<script>
import { props } from './props';
import { mpMixin } from '../../libs/mixin/mpMixin';
import { mixin } from '../../libs/mixin/mixin';
import { addUnit, addStyle, getPx, deepMerge, error } from '../../libs/function/index';
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
/**
* backTop 返回顶部
* @description 本组件一个用于长页面滑动一定距离后出现返回顶部按钮方便快速返回顶部的场景
* @tutorial https://uview-plus.jiangruyi.com/components/backTop.html
*
* @property {String} mode 返回顶部的形状circle-圆形square-方形 默认 'circle'
* @property {String} icon 自定义图标 默认 'arrow-upward' 见官方文档示例
* @property {String} text 提示文字
* @property {String | Number} duration 返回顶部滚动时间 默认 100
* @property {String | Number} scrollTop 滚动距离 默认 0
* @property {String | Number} top 距离顶部多少距离显示单位px 默认 400
* @property {String | Number} bottom 返回顶部按钮到底部的距离单位px 默认 100
* @property {String | Number} right 返回顶部按钮到右边的距离单位px 默认 20
* @property {String | Number} zIndex 层级 默认 9
* @property {Object<Object>} iconStyle 图标的样式对象形式 默认 {color: '#909399',fontSize: '19px'}
* @property {Object} customStyle 定义需要用到的外部样式
*
* @example <u-back-top :scrollTop="scrollTop"></u-back-top>
*/
export default {
name: 'u-back-top',
mixins: [mpMixin, mixin, props],
computed: {
backTopStyle() {
//
const style = {
bottom: addUnit(this.bottom),
right: addUnit(this.right),
width: '40px',
height: '40px',
position: 'fixed',
zIndex: 10,
}
return style
},
show() {
return getPx(this.scrollTop) > getPx(this.top)
},
contentStyle() {
const style = {}
let radius = 0
//
if(this.mode === 'circle') {
radius = '100px'
} else {
radius = '4px'
}
// nvue
style.borderTopLeftRadius = radius
style.borderTopRightRadius = radius
style.borderBottomLeftRadius = radius
style.borderBottomRightRadius = radius
return deepMerge(style, addStyle(this.customStyle))
}
},
emits: ["click"],
methods: {
backToTop() {
// #ifdef APP-NVUE
if (!this.$parent.$refs['u-back-top']) {
error(`nvue页面需要给页面最外层元素设置"ref='u-back-top'`)
}
dom.scrollToElement(this.$parent.$refs['u-back-top'], {
offset: 0
})
// #endif
// #ifndef APP-NVUE
uni.pageScrollTo({
scrollTop: 0,
duration: this.duration
});
// #endif
this.$emit('click')
}
}
}
</script>
<style lang="scss" scoped>
$u-back-top-flex:1 !default;
$u-back-top-height:100% !default;
$u-back-top-background-color:#E1E1E1 !default;
$u-back-top-tips-font-size:12px !default;
.u-back-top {
@include flex;
flex-direction: column;
align-items: center;
flex:$u-back-top-flex;
height: $u-back-top-height;
justify-content: center;
background-color: $u-back-top-background-color;
&__tips {
font-size:$u-back-top-tips-font-size;
transform: scale(0.8);
}
}
</style>

@ -0,0 +1,27 @@
/*
* @Author : LQ
* @Description :
* @version : 1.0
* @Date : 2021-08-20 16:44:21
* @LastAuthor : LQ
* @lastTime : 2021-08-23 19:51:50
* @FilePath : /u-view2.0/uview-ui/libs/config/props/badge.js
*/
export default {
// 徽标数组件
badge: {
isDot: false,
value: '',
show: true,
max: 999,
type: 'error',
showZero: false,
bgColor: null,
color: null,
shape: 'circle',
numberType: 'overflow',
offset: [],
inverted: false,
absolute: false
}
}

@ -0,0 +1,79 @@
import { defineMixin } from '../../libs/vue'
import defProps from '../../libs/config/props.js'
export const props = defineMixin({
props: {
// 是否显示圆点
isDot: {
type: Boolean,
default: () => defProps.badge.isDot
},
// 显示的内容
value: {
type: [Number, String],
default: () => defProps.badge.value
},
// 显示的内容
modelValue: {
type: [Number, String],
default: () => defProps.badge.modelValue
},
// 是否显示
show: {
type: Boolean,
default: () => defProps.badge.show
},
// 最大值,超过最大值会显示 '{max}+'
max: {
type: [Number, String],
default: () => defProps.badge.max
},
// 主题类型,error|warning|success|primary
type: {
type: String,
default: () => defProps.badge.type
},
// 当数值为 0 时,是否展示 Badge
showZero: {
type: Boolean,
default: () => defProps.badge.showZero
},
// 背景颜色,优先级比type高,如设置,type参数会失效
bgColor: {
type: [String, null],
default: () => defProps.badge.bgColor
},
// 字体颜色
color: {
type: [String, null],
default: () => defProps.badge.color
},
// 徽标形状,circle-四角均为圆角,horn-左下角为直角
shape: {
type: String,
default: () => defProps.badge.shape
},
// 设置数字的显示方式,overflow|ellipsis|limit
// overflow会根据max字段判断,超出显示`${max}+`
// ellipsis会根据max判断,超出显示`${max}...`
// limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数
numberType: {
type: String,
default: () => defProps.badge.numberType
},
// 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
offset: {
type: Array,
default: () => defProps.badge.offset
},
// 是否反转背景和字体颜色
inverted: {
type: Boolean,
default: () => defProps.badge.inverted
},
// 是否绝对定位
absolute: {
type: Boolean,
default: () => defProps.badge.absolute
}
}
})

@ -0,0 +1,176 @@
<template>
<text
v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)"
:class="[isDot ? 'u-badge--dot' : 'u-badge--not-dot', inverted && 'u-badge--inverted', shape === 'horn' && 'u-badge--horn', `u-badge--${type}${inverted ? '--inverted' : ''}`]"
:style="[addStyle(customStyle), badgeStyle]"
class="u-badge"
>{{ isDot ? '' :showValue }}</text>
</template>
<script>
import { props } from './props';
import { mpMixin } from '../../libs/mixin/mpMixin';
import { mixin } from '../../libs/mixin/mixin';
import { addStyle, addUnit } from '../../libs/function/index';
/**
* badge 徽标数
* @description 该组件一般用于图标右上角显示未读的消息数量提示用户点击有圆点和圆包含文字两种形式
* @tutorial https://uview-plus.jiangruyi.com/components/badge.html
*
* @property {Boolean} isDot 是否显示圆点 默认 false
* @property {String | Number} value 显示的内容
* @property {Boolean} show 是否显示 默认 true
* @property {String | Number} max 最大值超过最大值会显示 '{max}+' 默认999
* @property {String} type 主题类型error|warning|success|primary 默认 'error'
* @property {Boolean} showZero 当数值为 0 是否展示 Badge 默认 false
* @property {String} bgColor 背景颜色优先级比type高如设置type参数会失效
* @property {String} color 字体颜色 默认 '#ffffff'
* @property {String} shape 徽标形状circle-四角均为圆角horn-左下角为直角 默认 'circle'
* @property {String} numberType 设置数字的显示方式overflow|ellipsis|limit 默认 'overflow'
* @property {Array}} offset 设置badge的位置偏移格式为 [x, y]也即设置的为top和right的值absolute为true时有效
* @property {Boolean} inverted 是否反转背景和字体颜色默认 false
* @property {Boolean} absolute 是否绝对定位默认 false
* @property {Object} customStyle 定义需要用到的外部样式
* @example <u-badge :type="type" :count="count"></u-badge>
*/
export default {
name: 'u-badge',
mixins: [mpMixin, props, mixin],
computed: {
// badge
boxStyle() {
let style = {};
return style;
},
//
badgeStyle() {
const style = {}
if(this.color) {
style.color = this.color
}
if (this.bgColor && !this.inverted) {
style.backgroundColor = this.bgColor
}
if (this.absolute) {
style.position = 'absolute'
// offset
if(this.offset.length) {
// toprightoffsetrighttop
const top = this.offset[0]
const right = this.offset[1] || top
style.top = addUnit(top)
style.right = addUnit(right)
}
}
return style
},
showValue() {
switch (this.numberType) {
case "overflow":
return Number(this.value) > Number(this.max) ? this.max + "+" : this.value
break;
case "ellipsis":
return Number(this.value) > Number(this.max) ? "..." : this.value
break;
case "limit":
return Number(this.value) > 999 ? Number(this.value) >= 9999 ?
Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value /
1e3 * 100) / 100 + "k" : this.value
break;
default:
return Number(this.value)
}
},
},
methods: {
addStyle
}
}
</script>
<style lang="scss" scoped>
$u-badge-primary: $u-primary !default;
$u-badge-error: $u-error !default;
$u-badge-success: $u-success !default;
$u-badge-info: $u-info !default;
$u-badge-warning: $u-warning !default;
$u-badge-dot-radius: 100px !default;
$u-badge-dot-size: 8px !default;
$u-badge-dot-right: 4px !default;
$u-badge-dot-top: 0 !default;
$u-badge-text-font-size: 11px !default;
$u-badge-text-right: 10px !default;
$u-badge-text-padding: 2px 5px !default;
$u-badge-text-align: center !default;
$u-badge-text-color: #FFFFFF !default;
.u-badge {
border-top-right-radius: $u-badge-dot-radius;
border-top-left-radius: $u-badge-dot-radius;
border-bottom-left-radius: $u-badge-dot-radius;
border-bottom-right-radius: $u-badge-dot-radius;
@include flex;
line-height: $u-badge-text-font-size;
text-align: $u-badge-text-align;
font-size: $u-badge-text-font-size;
color: $u-badge-text-color;
&--dot {
height: $u-badge-dot-size;
width: $u-badge-dot-size;
}
&--inverted {
font-size: 13px;
}
&--not-dot {
padding: $u-badge-text-padding;
}
&--horn {
border-bottom-left-radius: 0;
}
&--primary {
background-color: $u-badge-primary;
}
&--primary--inverted {
color: $u-badge-primary;
}
&--error {
background-color: $u-badge-error;
}
&--error--inverted {
color: $u-badge-error;
}
&--success {
background-color: $u-badge-success;
}
&--success--inverted {
color: $u-badge-success;
}
&--info {
background-color: $u-badge-info;
}
&--info--inverted {
color: $u-badge-info;
}
&--warning {
background-color: $u-badge-warning;
}
&--warning--inverted {
color: $u-badge-warning;
}
}
</style>

@ -0,0 +1,27 @@
import { defineMixin } from '../../libs/vue'
import defProps from '../../libs/config/props.js'
export const propsBox = defineMixin({
props: {
// 背景色
bgColors: {
type: [Array],
default: ['#EEFCFF', '#FCF8FF', '#FDF8F2']
},
// 高度
height: {
type: [String],
default: "160px"
},
// 圆角
borderRadius: {
type: [String],
default: "6px"
},
// 间隔
gap: {
type: [String],
default: "15px"
},
}
})

@ -0,0 +1,91 @@
<template>
<view class="u-box" :style="[{height: height}, addStyle(customStyle)]">
<view class="u-box__left" :style="{borderRadius: borderRadius, backgroundColor: bgColors[0]}">
<slot name="left"></slot>
</view>
<view class="u-box__gap" :style="{width: gap, height: height}"></view>
<view class="u-box__right">
<view class="u-box__right-top" :style="{borderRadius: borderRadius, backgroundColor: bgColors[1]}">
<slot name="rightTop">右上</slot>
</view>
<view class="u-box__right-gap" :style="{height: gap}"></view>
<view class="u-box__right-bottom" :style="{borderRadius: borderRadius, backgroundColor: bgColors[2]}">
<slot name="rightBottom">右下</slot>
</view>
</view>
</view>
</template>
<script>
import { propsBox } from './props';
import { mpMixin } from '../../libs/mixin/mpMixin';
import { mixin } from '../../libs/mixin/mixin';
import { addStyle } from '../../libs/function/index';
import test from '../../libs/function/test';
/**
* box 盒子
* @description box盒子一般为左边一个盒子右侧两个等高的半盒组成常用于App首页座位重点突出
* @tutorial https://uview-plus.jiangruyi.com/components/box.html
* @property {Array} bgColors 背景色
* @property {String} height 高度
* @property {String} borderRadius 圆角
* @property {Object} customStyle 定义需要用到的外部样式
*
* @event {Function} click 点击cell列表时触发
* @example <up-box colors=['blue', 'red', 'yellow'] height="200px"></up-box>
*/
export default {
name: 'up-box',
data() {
return {
}
},
mixins: [mpMixin, mixin, propsBox],
computed: {
},
emits: [],
methods: {
addStyle,
}
}
</script>
<style lang="scss" scoped>
.u-box {
/* #ifndef APP-NVUE */
/* #endif */
@include flex();
flex: 1;
&__left {
@include flex();
justify-content: center;
align-items: center;
flex: 1;
}
&__gap {
@include flex();
flex-direction: column;
}
&__right {
@include flex();
flex-direction: column;
flex: 1;
}
&__right-top {
@include flex();
flex: 1;
justify-content: center;
align-items: center;
}
&__right-bottom {
@include flex();
flex: 1;
justify-content: center;
align-items: center;
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save