master
yujialong 2 years ago
commit 1cd4b94cfe
  1. 1
      .gitignore
  2. 16
      .hbuilderx/launch.json
  3. 36
      App.vue
  4. 1
      README.md
  5. 54
      apis/modules/client.js
  6. 38
      apis/modules/order.js
  7. 26
      apis/modules/parner.js
  8. 6
      apis/modules/product.js
  9. 52
      apis/modules/user.js
  10. 89
      apis/request.js
  11. 40
      components/empty/empty.vue
  12. 130
      components/filter-popup/components/mask.vue
  13. 510
      components/filter-popup/components/popup.vue
  14. 155
      components/filter-popup/detail.md
  15. 466
      components/filter-popup/filter-popup.vue
  16. 140
      components/uni-section/uni-section.vue
  17. 10
      config/product.js
  18. 21
      config/request.js
  19. 16
      directives/auth.js
  20. 12
      directives/index.js
  21. 14
      index.html
  22. 35
      libs/WXBizDataCrypt.js
  23. 107
      libs/util.js
  24. 39
      main.js
  25. 60
      manifest.json
  26. 329
      order/addCourse/addCourse.vue
  27. 266
      order/curClient/curClient.vue
  28. 420
      order/editCourse/editCourse.vue
  29. 644
      order/orderDetail/orderDetail.vue
  30. 270
      order/ordered/ordered.vue
  31. 311
      order/orders/orders.vue
  32. 12
      package.json
  33. 231
      pages.json
  34. 61
      pages/account/account.vue
  35. 51
      pages/addStaff/addStaff.vue
  36. 372
      pages/clientDetail/clientDetail.vue
  37. 244
      pages/clients/clients.vue
  38. 113
      pages/email/email.vue
  39. 349
      pages/index/index.vue
  40. 236
      pages/login/login.vue
  41. 62
      pages/password/password.vue
  42. 140
      pages/person/person.vue
  43. 119
      pages/phone/phone.vue
  44. 199
      pages/plans/plans.vue
  45. 217
      pages/products/products.vue
  46. 108
      pages/qrcode/qrcode.vue
  47. 105
      pages/send/send.vue
  48. 90
      pages/setting/setting.vue
  49. 162
      pages/teams/teams.vue
  50. 32
      static/iconfont/iconfont.css
  51. BIN
      static/iconfont/iconfont.ttf
  52. BIN
      static/image/arrow-down.png
  53. BIN
      static/image/avatar.png
  54. BIN
      static/image/index/index1.png
  55. BIN
      static/image/index/index10.png
  56. BIN
      static/image/index/index2.png
  57. BIN
      static/image/index/index3.png
  58. BIN
      static/image/index/index4.png
  59. BIN
      static/image/index/index5.png
  60. BIN
      static/image/index/index6.png
  61. BIN
      static/image/index/index7.png
  62. BIN
      static/image/index/index8.png
  63. BIN
      static/image/index/index9.png
  64. BIN
      static/image/login1.png
  65. BIN
      static/image/login2.png
  66. BIN
      static/image/logo.png
  67. BIN
      static/image/none.png
  68. BIN
      static/image/person-bg.png
  69. BIN
      static/image/person1.png
  70. BIN
      static/image/person2.png
  71. BIN
      static/image/person3.png
  72. BIN
      static/image/person4.png
  73. BIN
      static/image/phone.png
  74. BIN
      static/image/qrcode.png
  75. BIN
      static/image/school.png
  76. BIN
      static/image/tab1-1.png
  77. BIN
      static/image/tab1.png
  78. BIN
      static/image/tab2-1.png
  79. BIN
      static/image/tab2.png
  80. BIN
      static/image/tab3-1.png
  81. BIN
      static/image/tab3.png
  82. BIN
      static/image/trash.png
  83. BIN
      static/image/wechat.png
  84. 221
      styles/common.scss
  85. 3
      uni.scss
  86. 44
      uni_modules/Sansnn-uQRCode/changelog.md
  87. 241
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/bridge/bridge-weex.js
  88. 18
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-2d/FillStyleLinearGradient.js
  89. 8
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-2d/FillStylePattern.js
  90. 17
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-2d/FillStyleRadialGradient.js
  91. 666
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-2d/RenderingContext.js
  92. 11
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-webgl/ActiveInfo.js
  93. 21
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-webgl/Buffer.js
  94. 21
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-webgl/Framebuffer.js
  95. 298
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-webgl/GLenum.js
  96. 142
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-webgl/GLmethod.js
  97. 23
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-webgl/GLtype.js
  98. 21
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-webgl/Program.js
  99. 21
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-webgl/Renderbuffer.js
  100. 1191
      uni_modules/Sansnn-uQRCode/components/u-qrcode/gcanvas/context-webgl/RenderingContext.js
  101. Some files were not shown because too many files have changed in this diff Show More

1
.gitignore vendored

@ -0,0 +1 @@
unpackage

@ -0,0 +1,16 @@
{ // launch.json configurations app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtypelocalremote, localremote
"version": "0.0",
"configurations": [{
"default" :
{
"launchtype" : "local"
},
"mp-weixin" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}

@ -0,0 +1,36 @@
<script>
export default {
onLaunch: function() {
// uni.setEnableDebug({
// enableDebug: true
// })
},
onShow: function() {
console.log('App Launch');
const updateManager = uni.getUpdateManager();
//
updateManager.onCheckForUpdate(function (res) {
//
console.log('onCheckForUpdate', res.hasUpdate);
});
//
updateManager.onUpdateReady(function (res) {
updateManager.applyUpdate()
});
updateManager.onUpdateFailed(function (res) {
console.log('onUpdateFailed', res);
//
uni.showModal({
title: '已经有新版本了哟~',
content: '新版本已经上线啦~,请您删除当前小程序,重新搜索打开哟~'
});
})
}
}
</script>
<style lang="scss">
@import '@/uni_modules/uni-scss/index.scss';
</style>

@ -0,0 +1 @@
# 或然中台小程序端

@ -0,0 +1,54 @@
import request from '@/apis/request.js'
const { get, post } = request
export const queryCustomer = (data) => {
return post('nakadai/nakadai/customer/queryCustomer', data)
}
export const list = (data) => {
return post('nakadai/nakadai/applets/customer/getCustomerListBasedOnBusinessManagerId', data)
}
export const all = (data) => {
return post('nakadai/nakadai/applets/customer/customerList', data)
}
export const queryCustomerDetails = (data) => {
return get('nakadai/nakadai/customer/queryCustomerDetails', data)
}
export const querySchool = (data) => {
return get('nakadai/nakadai/school/querySchool', data)
}
export const queryIndustryClass = (data) => {
return get('nakadai/nakadai/hrIndustryClass/queryIndustryClass', data)
}
export const queryIndustry = (data) => {
return get('nakadai/nakadai/hrIndustry/queryIndustry', data)
}
export const queryCustomerIsExists = (data) => {
return get('nakadai/nakadai/customer/queryCustomerIsExists', data)
}
export const addCustomer = (data) => {
return post('nakadai/nakadai/customer/addCustomer', data)
}
export const updateCustomer = (data) => {
return post('nakadai/nakadai/customer/updateCustomer', data)
}
export const getProductsSubscribedByCustomers = (data) => {
return get('nakadai/nakadai/customer/getProductsSubscribedByCustomers', data)
}
export const getTeamsByAccountId = (data) => {
return post('nakadai/nakadai/applets/customer/getTeamsByAccountId', data)
}
export const getTheBusinessManagerIdsUnderTheTeam = id => {
return post(`nakadai/nakadai/applets/customer/getTheBusinessManagerIdsUnderTheTeam?id=${id}`)
}

@ -0,0 +1,38 @@
import request from '@/apis/request.js'
const { get, post } = request
export const getOrderOtherTime = (data) => {
return post('nakadai/nakadai/orderOther/getOrderOtherTime', data)
}
export const add = (data) => {
return post('nakadai/nakadai/order/add', data)
}
export const update = (data) => {
return post('nakadai/nakadai/order/update', data)
}
export const getDetail = (data) => {
return get('nakadai/nakadai/order/get', data)
}
export const renew = (data) => {
return post('nakadai/nakadai/orderOther/renew', data)
}
export const list = (data) => {
return post('nakadai/nakadai/applets/order/orderList', data)
}
export const orderList = (data) => {
return post('nakadai/nakadai/order/list', data)
}
export const del = (data) => {
return post('nakadai/nakadai/order/delete', data)
}
export const miniProgramOrderRecord = (data) => {
return post('nakadai/nakadai/applets/order/miniProgramOrderRecord', data)
}

@ -0,0 +1,26 @@
import request from '@/apis/request.js'
const { get, post } = request
export const savePartnerAccount = (data) => {
return post('nakadai/nakadai/partnerAccount/savePartnerAccount', data)
}
export const teamList = (data) => {
return post('nakadai/nakadai/partnerAccount/partnerAccountList', data)
}
export const generateInvitationCode = accountId => {
return post(`nakadai/nakadai/partnerAccount/generateInvitationCode?accountId=${accountId}`)
}
export const treeList = (data) => {
return post('nakadai/nakadai/partnerClassification/treeList', data)
}
export const my = (data) => {
return get('nakadai/nakadai/partner-team/my', data)
}
export const mailFileSend = (data) => {
return post('nakadai/nakadai/partnerAccount/mailFileSend', data)
}

@ -0,0 +1,6 @@
import request from '@/apis/request.js'
const { get, post } = request
export const AppletsDataProductList = (data) => {
return post('nakadai/nakadai/dataProduct/AppletsDataProductList', data)
}

@ -0,0 +1,52 @@
import request from '@/apis/request.js'
const { get, post } = request
export const login = (data) => {
return post('users/users/user/weChatAppletCallback', data)
}
export const userBinding = (data) => {
return post('users/users/user/userBinding', data)
}
export const examinePassword = (data) => {
return post('users/users/userAccount/examinePassword', data)
}
export const getUserRolesPermissionMenu = (data) => {
return get('users/users/user-role/getUserRolesPermissionMenu', data)
}
export const updatePersonCenter = (data) => {
return post('users/users/userAccount/updatePersonCenter', data)
}
export const queryUserInfoDetails = () => {
return get('users/users/userAccount/queryUserInfoDetails')
}
export const updateMyEmail = (data) => {
return post('nakadai/nakadai/partner-team/updateMyEmail', data)
}
export const mailCodeSend = (data) => {
return post('nakadai/nakadai/partner-team/mailCodeSend', data)
}
export const changeAccount = account => {
return post(`users/users/applets/mine/changeAccount?account=${account}`)
}
export const changePhoneNumber = (phone, code) => {
return post(`users/users/applets/mine/changePhoneNumber?phone=${phone}&code=${code}`)
}
export const checkIfAnAccountExists = account => {
return post(`users/users/applets/mine/checkIfAnAccountExists?account=${account}`)
}
export const checkIfThePhoneNumberExists = phone => {
return post(`users/users/applets/mine/checkIfThePhoneNumberExists?phone=${phone}`)
}
export const updateUserAvatars = `http://39.108.250.202:9000/users/users/userAccount/updateUserAvatars`

@ -0,0 +1,89 @@
import config from '@/config/request'
let HTTP_COUNT = 0 // loading次数
const request = options => {
HTTP_COUNT++
if (config.showLoading) {
// 请求数据时的loading
// uni.showToast({
// title: '加载中',
// duration: 200,
// icon: 'loading'
// })
}
const header = Object.assign({}, config.headers, {
token: uni.getStorageSync('token')
})
const otherUrl = ['getTeamsByAccountId']
return new Promise((resolve, reject)=>{
const { url } = options
uni.request({
header,
// url: (otherUrl.map(e => config.baseURL.includes(e)) ? 'http://192.168.31.137:9000/' : config.baseURL) + url,
url: config.baseURL + url,
method: options.method || 'GET', // 请求类型,默认为GET
data: options.data || {}, // 请求参数,默认空对象
success: ({ data }) => {
const { status, message } = data
// 状态判断,根据后台定义并提示
if (status === 200) {
resolve(data)
} else if (status == 401) {
// 登录过期
uni.clearStorageSync()
uni.showToast({
title: message,
icon: 'none'
})
setTimeout(() => {
uni.navigateTo({
url: '../login/login'
})
}, 1500)
reject(data)
} else if (!status) {
resolve(data)
} else {
uni.showToast({
title: message,
icon: 'none'
})
reject(data)
}
},
fail: err => {
uni.showToast({
title: '请求失败!',
icon: 'none'
})
reject(err)
},
complete: () => {
if (config.showLoading) {
// HTTP_COUNT--
// HTTP_COUNT || uni.hideLoading()
}
}
})
})
}
const get = (url, data, options = {}) => {
options.method = 'GET'
options.data = data
options.url = url
return request(options)
}
const post = (url, data, options = {}) => {
options.method = 'POST'
options.data = data
options.url = url
return request(options)
}
export default {
request,
get,
post
}

@ -0,0 +1,40 @@
<template>
<view class="none">
<image src="../../static/image/none.png" mode="widthFix"></image>
<view class="text">{{ text }}</view>
</view>
</template>
<script>
export default {
props: {
text: {
type: String,
default: '暂无数据'
},
},
data() {
return {
}
},
methods: {
}
}
</script>
<style scoped lang="scss">
.none {
padding-top: 100rpx;
text-align: center;
image {
width: 426rpx;
margin-bottom: 52rpx;
}
.text {
font-size: 28rpx;
color: #333;
}
}
</style>

@ -0,0 +1,130 @@
<template>
<view
class="u-mask"
hover-stop-propagation
:style="[maskStyle, zoomStyle]"
@tap="click"
@touchmove.stop.prevent="() => {}"
:class="{
'u-mask-zoom': zoom,
'u-mask-show': show,
}"
>
<slot />
</view>
</template>
<script>
/**
* mask 遮罩
* @description 创建一个遮罩层用于强调特定的页面元素并阻止用户对遮罩下层的内容进行操作一般用于弹窗场景
* @tutorial https://www.uviewui.com/components/mask.html
* @property {Boolean} show 是否显示遮罩默认false
* @property {String Number} z-index z-index 层级默认1070
* @property {Object} custom-style 自定义样式对象见上方说明
* @property {String Number} duration 动画时长单位毫秒默认300
* @property {Boolean} zoom 是否使用scale对这招进行缩放默认true
* @property {Boolean} mask-click-able 遮罩是否可点击为false时点击不会发送click事件默认true
* @event {Function} click mask-click-able为true时点击遮罩发送此事件
* @example <u-mask :show="show" @click="show = false"></u-mask>
*/
export default {
name: "u-mask",
props: {
//
show: {
type: Boolean,
default: false,
},
// z-index
zIndex: {
type: [Number, String],
default: "",
},
//
customStyle: {
type: Object,
default() {
return {};
},
},
// 使使zoomscale
zoom: {
type: Boolean,
default: true,
},
// ms
duration: {
type: [Number, String],
default: 300,
},
//
maskClickAble: {
type: Boolean,
default: true,
},
},
data() {
return {
zoomStyle: {
transform: "",
},
scale: "scale(1.2, 1.2)",
};
},
watch: {
show(n) {
if (n && this.zoom) {
// scale1(1.2)
this.zoomStyle.transform = "scale(1, 1)";
} else if (!n && this.zoom) {
// scale1.2(1)
this.zoomStyle.transform = this.scale;
}
},
},
computed: {
maskStyle() {
let style = {};
style.backgroundColor = "rgba(0, 0, 0, 0.6)";
if (this.show)
style.zIndex = this.zIndex ? this.zIndex : this.$u.zIndex.mask;
else style.zIndex = -1;
style.transition = `all ${this.duration / 1000}s ease-in-out`;
//
if (Object.keys(this.customStyle).length)
style = {
...style,
...this.customStyle,
};
return style;
},
},
methods: {
click() {
if (!this.maskClickAble) return;
this.$emit("click");
},
},
};
</script>
<style lang="scss" scoped>
.u-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
transition: transform 0.3s;
}
.u-mask-show {
opacity: 1;
}
.u-mask-zoom {
transform: scale(1.2, 1.2);
}
</style>

@ -0,0 +1,510 @@
<template>
<view
v-if="visibleSync"
:style="[
customStyle,
{
zIndex: uZindex - 1,
},
]"
class="u-drawer"
hover-stop-propagation
>
<z-mask
:duration="duration"
:custom-style="maskCustomStyle"
:maskClickAble="maskCloseAble"
:z-index="uZindex - 2"
:show="showDrawer && mask"
@click="maskClick"
></z-mask>
<view
class="u-drawer-content"
@tap="modeCenterClose(mode)"
:class="[
safeAreaInsetBottom ? 'safe-area-inset-bottom' : '',
'u-drawer-' + mode,
showDrawer ? 'u-drawer-content-visible' : '',
zoom && mode == 'center' ? 'u-animation-zoom' : '',
]"
@transitionend="transitionend"
@touchmove.stop.prevent
@tap.stop.prevent
:style="[style]"
>
<view
class="u-mode-center-box"
@tap.stop.prevent
@touchmove.stop.prevent
v-if="mode == 'center'"
:style="[centerStyle]"
>
<u-icon
@click="close"
v-if="closeable"
class="u-close"
:class="['u-close--' + closeIconPos]"
:name="closeIcon"
:color="closeIconColor"
:size="closeIconSize"
></u-icon>
<scroll-view class="u-drawer__scroll-view" scroll-y="true">
<slot />
</scroll-view>
<slot name="fixedContent" />
</view>
<scroll-view class="u-drawer__scroll-view" scroll-y="true" v-else>
<slot />
</scroll-view>
<view @tap="close" class="u-close" :class="['u-close--' + closeIconPos]">
<u-icon
v-if="mode != 'center' && closeable"
:name="closeIcon"
:color="closeIconColor"
:size="closeIconSize"
></u-icon>
</view>
</view>
</view>
</template>
<script>
/**
* popup 弹窗
* @description 弹出层容器用于展示弹窗信息提示等内容支持上右和中部弹出组件只提供容器内部内容由用户自定义
* @tutorial https://www.uviewui.com/components/popup.html
* @property {String} mode 弹出方向默认left
* @property {Boolean} mask 是否显示遮罩默认true
* @property {Stringr | Number} length mode=left | 见官网说明默认auto
* @property {Boolean} zoom 是否开启缩放动画只在mode为center时有效默认true
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配默认false
* @property {Boolean} mask-close-able 点击遮罩是否可以关闭弹出层默认true
* @property {Object} custom-style 用户自定义样式
* @property {Stringr | Number} negative-top 中部弹出时往上偏移的值
* @property {Numberr | String} border-radius 弹窗圆角值默认0
* @property {Numberr | String} z-index 弹出内容的z-index值默认1075
* @property {Boolean} closeable 是否显示关闭图标默认false
* @property {String} close-icon 关闭图标的名称只能uView的内置图标
* @property {String} close-icon-pos 自定义关闭图标位置默认top-right
* @property {String} close-icon-color 关闭图标的颜色默认#909399
* @property {Number | String} close-icon-size 关闭图标的大小单位rpx默认30
* @event {Function} open 弹出层打开
* @event {Function} close 弹出层收起
* @example <u-popup v-model="show"><view>出淤泥而不染濯清涟而不妖</view></u-popup>
*/
import ZMask from "./mask.vue";
export default {
name: "popup",
components: {
ZMask,
},
props: {
/**
* 显示状态
*/
show: {
type: Boolean,
default: false,
},
/**
* 弹出方向left|right|top|bottom|center
*/
mode: {
type: String,
default: "left",
},
/**
* 是否显示遮罩
*/
mask: {
type: Boolean,
default: true,
},
// (mode=left|right)(mode=top|bottom)rpx"auto"
// "50%"
length: {
type: [Number, String],
default: "auto",
},
// mode=center
zoom: {
type: Boolean,
default: true,
},
// iPhoneX
safeAreaInsetBottom: {
type: Boolean,
default: false,
},
//
maskCloseAble: {
type: Boolean,
default: true,
},
//
customStyle: {
type: Object,
default() {
return {};
},
},
value: {
type: Boolean,
default: false,
},
// 使Pickerkeyboard
// v-modelprops
popup: {
type: Boolean,
default: true,
},
// rpx
borderRadius: {
type: [Number, String],
default: 0,
},
zIndex: {
type: [Number, String],
default: "10075",
},
//
closeable: {
type: Boolean,
default: false,
},
// uView
closeIcon: {
type: String,
default: "close",
},
// top-lefttop-rightbottom-leftbottom-right
closeIconPos: {
type: String,
default: "top-right",
},
//
closeIconColor: {
type: String,
default: "#909399",
},
// rpx
closeIconSize: {
type: [String, Number],
default: "30",
},
// rpx"auto"
// "50%"length
width: {
type: String,
default: "",
},
// rpx"auto"
// "50%"length
height: {
type: String,
default: "",
},
// margin-topmode=center
negativeTop: {
type: [String, Number],
default: 0,
},
//
maskCustomStyle: {
type: Object,
default() {
return {};
},
},
// ms
duration: {
type: [String, Number],
default: 250,
},
//
centerPupBg: {
type: String,
default: "#fff",
},
bgColor: {
type: String,
default: "#fff",
},
},
data() {
return {
visibleSync: false,
showDrawer: false,
timer: null,
closeFromInner: false, // value
};
},
computed: {
// mode(mode = left|right)(mode = top|bottom)
style() {
let style = {};
// translate
if (this.mode == "left" || this.mode == "right") {
style = {
width: this.width
? this.getUnitValue(this.width)
: this.getUnitValue(this.length),
height: "100%",
transform: `translate3D(${
this.mode == "left" ? "-100%" : "100%"
},0px,0px)`,
background: this.bgColor,
};
} else if (this.mode == "top" || this.mode == "bottom") {
style = {
width: "100%",
height: this.height
? this.getUnitValue(this.height)
: this.getUnitValue(this.length),
transform: `translate3D(0px,${
this.mode == "top" ? "-100%" : "100%"
},0px)`,
background: this.bgColor,
};
}
style.zIndex = this.uZindex;
// borderRadius
if (this.borderRadius) {
switch (this.mode) {
case "left":
style.borderRadius = `0 ${this.borderRadius}rpx ${this.borderRadius}rpx 0`;
break;
case "top":
style.borderRadius = `0 0 ${this.borderRadius}rpx ${this.borderRadius}rpx`;
break;
case "right":
style.borderRadius = `${this.borderRadius}rpx 0 0 ${this.borderRadius}rpx`;
break;
case "bottom":
style.borderRadius = `${this.borderRadius}rpx ${this.borderRadius}rpx 0 0`;
break;
default:
}
//
style.overflow = "hidden";
}
if (this.duration)
style.transition = `all ${this.duration / 1000}s linear`;
return style;
},
//
centerStyle() {
let style = {};
style.width = this.width
? this.getUnitValue(this.width)
: this.getUnitValue(this.length);
// auto
style.height = this.height ? this.getUnitValue(this.height) : "auto";
style.zIndex = this.uZindex;
// style.marginTop = `-${this.$u.addUnit(this.negativeTop)}`;
style.background = this.centerPupBg;
if (this.borderRadius) {
style.borderRadius = `${this.borderRadius}rpx`;
//
style.overflow = "hidden";
}
return style;
},
// z-index
uZindex() {
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
},
},
watch: {
value(val) {
if (val) {
this.open();
} else if (!this.closeFromInner) {
this.close();
}
this.closeFromInner = false;
},
},
mounted() {
// valuetruepopup
this.value && this.open();
},
methods: {
//
transitionend(e) {
// console.log(e, "popup");
},
// rpx
getUnitValue(val) {
if (/(%|px|rpx|auto)$/.test(val)) return val;
else return val + "rpx";
},
//
maskClick() {
this.close();
},
close() {
// valuewatchvalueclose
// @close
this.closeFromInner = true;
this.change("showDrawer", "visibleSync", false);
},
// .u-drawer-content
// mode=center
modeCenterClose(mode) {
if (mode != "center" || !this.maskCloseAble) return;
this.close();
},
open() {
this.change("visibleSync", "showDrawer", true);
},
//
//
change(param1, param2, status) {
// this.popupfalsepickeractionsheetpopup
if (this.popup == true) {
this.$emit("input", status);
}
this[param1] = status;
if (status) {
// #ifdef H5 || MP
this.timer = setTimeout(() => {
this[param2] = status;
this.$emit(status ? "open" : "close");
}, 50);
// #endif
// #ifndef H5 || MP
this.$nextTick(() => {
this[param2] = status;
this.$emit(status ? "open" : "close");
});
// #endif
} else {
this.timer = setTimeout(() => {
this[param2] = status;
this.$emit(status ? "open" : "close");
}, this.duration);
}
},
},
};
</script>
<style scoped lang="scss">
@mixin vue-flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: $direction;
/* #endif */
}
.u-drawer {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
.u-drawer-content {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: absolute;
z-index: 1003;
transition: all 0.25s linear;
}
.u-drawer__scroll-view {
width: 100%;
height: 100%;
}
.u-drawer-left {
top: 0;
bottom: 0;
left: 0;
}
.u-drawer-right {
right: 0;
top: 0;
bottom: 0;
}
.u-drawer-top {
top: 0;
left: 0;
right: 0;
}
.u-drawer-bottom {
bottom: 0;
left: 0;
right: 0;
}
.u-drawer-center {
@include vue-flex;
flex-direction: column;
bottom: 0;
left: 0;
right: 0;
top: 0;
justify-content: center;
align-items: center;
opacity: 0;
z-index: 99999;
}
.u-mode-center-box {
min-width: 100rpx;
min-height: 100rpx;
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: relative;
}
.u-drawer-content-visible.u-drawer-center {
transform: scale(1);
opacity: 1;
}
.u-animation-zoom {
transform: scale(1.15);
}
.u-drawer-content-visible {
transform: translate3D(0px, 0px, 0px) !important;
}
.u-close {
position: absolute;
z-index: 3;
}
.u-close--top-left {
top: 30rpx;
left: 30rpx;
}
.u-close--top-right {
top: 30rpx;
right: 30rpx;
}
.u-close--bottom-left {
bottom: 30rpx;
left: 30rpx;
}
.u-close--bottom-right {
right: 30rpx;
bottom: 30rpx;
}
</style>

@ -0,0 +1,155 @@
## 导入即用 全端支持
### 有问题 + wx : zy597172583 标注来意 可评论 看到及时回复
1.使用方式
```javascript
<template>
<filter-popup :data="filterData" :form.sync="filterForm" v-model="popup.filter" title="全部筛选" height="1104rpx" @finsh="subFinsh"></filter-popup>
</template>
<script>
import FilterPopup from "@/components/filter-popup/filter-popup";
export default {
components: {
FilterPopup,
},
data() {
return {
//筛选表单数据
filterData: [
{
children: false,//是否有子项
title: "意向度",
key: "intention_type", //键名 接收对象名字
keyValue: "value", //获取的值是哪个
isRadio: true, //是否单选 否则多选
data: [
{
title: "一般意向",
id: 1,
value: 1,
},
{
title: "中意向度",
id: 2,
value: 2,
},
{
title: "高意向度",
id: 3,
value: 3,
},
],
},
{
children: false,//是否有子项
title: "手机号码",
key: "is_bind_phone", //键名 接收对象名字
keyValue: "value", //获取的值是哪个
isRadio: true, //是否单选 否则多选
data: [
{
title: "未绑定",
value: 0,
},
{
title: "已绑定",
value: 1,
},
],
},
{
children: false,//是否有子项
title: "企微好友",
key: "is_work_customer", //键名 接收对象名字
keyValue: "value", //获取的值是哪个
isRadio: true, //是否单选 否则多选
data: [
{
title: "未添加",
value: 0,
},
{
title: "已添加",
value: 1,
},
],
},
{
children: true,//是否有子项
isRadio: false, //是否单选
title: "标签内容",
key: "label", //键名 接收对象名字
keyValue: "id", //获取的值是哪个
data: [
{
title: "客户重要程度",
id: 22,
children: [
{
title: "一般意向2",
id: 32,
value: 1,
},
{
title: "一般意向3",
id: 12,
value: 1,
},
],
},
{
title: "客户重要程度2",
id: 122,
children: [
{
title: "一般意向2",
id: 43,
value: 1,
},
{
title: "一般意向3",
id: 23,
value: 1,
},
],
},
],
},
], //筛选数据
filterForm: {}, //选中的表单
};
},
}
```
2.组件props
| 参数名 | 类型 | 介绍 |
| ---------- | ------- | ------------------------------------------------- |
| form | Object | .sync双向绑定的表单值 , 可传入显示初始哪些被选中 |
| data | Array | 动态渲染选项的数据数组 |
| title | String | 标题 |
| height | String | 弹出层高度 单位 rpx px upx 百分比 vw等 |
| themeColor | String | 组件主体颜色 默认:\#0066ff |
| mask | Boolean | 是否显示弹出遮盖层 |
3.data 参数
| 参数名 | 类型 | 是否必填 | 介绍 |
| -------- | ------- | -------- | ------------------------------------------------------------ |
| children | Boolean | 是 | 是否有子项 |
| data | Array | 是 | 渲染出来的选项数据 |
| isRadio | Boolean | 是 | 是否单选 单个选项指定,单选还是多选 |
| title | String | 是 | 标签内容标题 |
| key | String | 是 | 接收对象名字 会作为@finsh返回对象的键名 |
| keyValue | String | 是 | 获取的值是哪个 自定义指定获取哪个键值 value还是id或者自己定义的 |
4.事件
| 事件名 | 返回参数 | 简介 |
| ------ | -------- | ----------------------------------------- |
| finsh | Object | 点击确定时触发 返回参数为选中值的对象数组 |
| close | 无 | 组件点击关闭时触发 |
![image-20210730095456900](https://yzhsaas-cdn.qietongvip.com/asd.png)

@ -0,0 +1,466 @@
<template>
<popup
:mask="mask"
border-radius="50"
v-model="acceptValue"
mode="bottom"
class="filter-popup"
:height="height"
@close="close"
:style="{ '--color': themeColor }"
:mask-custom-style="{ background: 'rgba(0, 0, 0, 0.7)' }"
>
<view class="top-title flex-row-sb" v-if="showTop">
<view></view> <view class="popup-title flex-row-c-c">{{ title }}</view
><text class="saasIcon flex-row-c-c" @click="close">&#xe60c;</text>
</view>
<scroll-view class="select-scroll" scroll-y :style="{ height: `calc( ${height} - 120rpx - 152rpx )` }">
<view class="select-main">
<view class="select-item" v-for="(item, index) in data" :key="index">
<view class="title"> {{ item.title }} </view>
<view class="tag-list" v-if="!item.children">
<view
class="tag-item"
:class="[acceptForm[item.key].includes(item2.value) ? 'select-on' : '']"
v-for="(item2, index2) in item.data"
:key="index2"
@click="selectTagBuyValueOrId(item2, item.key, item.keyValue, item.isRadio)"
>
{{ item2.title }}
</view>
</view>
<!-- 有childer -->
<view class="select-childer" v-else v-for="item2 in item.data" :key="item2.id">
<view class="childer-title flex-row--c">{{ item2.title }}</view>
<view class="tag-list">
<view
class="tag-item"
:class="[acceptForm[item.key].includes(item3.id) ? 'select-on' : '']"
v-for="item3 in item2.children"
:key="item3.id"
@click="selectTagBuyValueOrId(item3, item.key, item.keyValue, item.isRadio)"
>
{{ item3.title }}
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<view class="filter-button flex-row-c">
<view class="btn flex-row">
<view class="btn-1 flex-row-c-c" @click="reset">重置</view>
<view class="btn-2 flex-row-c-c" @click="finsh">完成</view>
</view>
</view>
</popup>
</template>
<script>
import Popup from './components/popup.vue';
export default {
components:{
Popup
},
name: "filter-popup",
props: {
//
form: {
type: Object,
default: () => {},
},
//
themeColor: {
type: String,
default: "#0066ff",
},
//
data: {
type: Array,
default: () => [],
},
//
title: {
type: String,
default: "请选择",
},
value: {
type: Boolean,
default: false,
},
mask: {
type: Boolean,
default: true,
},
height: {
type: String,
default: "600rpx",
},
//退
showTop: {
type: Boolean,
default: true,
},
},
computed: {
//
acceptForm: {
get() {
this.originForm = JSON.parse(JSON.stringify(this.form))
if (Object.keys(this.form).length) {
return this.form;
} else {
let obj = {};
this.data.forEach((item) => {
obj[item.key] = [];
});
return obj;
}
},
set(nval) {
// console.log("set Form :>> ", nval);
this.$emit("update:form", nval);
},
},
acceptValue: {
get() {
return this.value;
},
set(nval) {
this.$emit("input", nval);
},
},
},
data() {
return {
originForm: {}
}
},
methods: {
//value id
selectTagBuyValueOrId(item, key, keyValue, isRadio) {
//
if (isRadio) {
if (keyValue == "value") {
if (this.acceptForm[key].some((value) => value === item.value)) {
this.acceptForm[key] = this.acceptForm[key].filter((value) => value !== item.value);
return;
}
this.acceptForm[key] = [];
this.acceptForm[key].push(item.value);
} else {
if (this.acceptForm[key].some((id) => id === item.id)) {
this.acceptForm[key] = this.acceptForm[key].filter((id) => id !== item.id);
return;
}
this.acceptForm[key] = [];
this.acceptForm[key].push(item.id);
}
} else {
if (keyValue == "value") {
this.acceptForm[key].some((value) => value === item.value)
? (this.acceptForm[key] = this.acceptForm[key].filter((value) => value !== item.value))
: this.acceptForm[key].push(item.value);
} else {
this.acceptForm[key].some((id) => id === item.id)
? (this.acceptForm[key] = this.acceptForm[key].filter((id) => id !== item.id))
: this.acceptForm[key].push(item.id);
}
}
this.acceptForm = this.acceptForm;
},
//
finsh() {
this.$emit("finsh", this.acceptForm);
this.$emit("input", false);
},
close() {
this.$emit("input", false);
this.$emit("close");
},
//
reset() {
this.acceptForm = {}
},
},
};
</script>
<style lang="scss" scoped>
@font-face {
font-family: 'iconfont'; /* Project id 2729410 */
src: url('https://at.alicdn.com/t/font_2729410_3nhq3ibbcqg.woff2?t=1628330097695') format('woff2'),
url('https://at.alicdn.com/t/font_2729410_3nhq3ibbcqg.woff?t=1628330097695') format('woff'),
url('https://at.alicdn.com/t/font_2729410_3nhq3ibbcqg.ttf?t=1628330097695') format('truetype');
}
.saasIcon {
font-family: "iconfont" !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.2px;
-moz-osx-font-smoothing: grayscale;
}
.filter-popup {
color: #000000;
.top-title {
font-weight: bold;
height: 90rpx;
margin-left: 70rpx;
font-size: 30rpx;
margin-top: 20rpx;
.popup-title {
height: 100%;
}
.saasIcon {
// width: 150rpx;
height: 100%;
padding-right: 70rpx;
}
}
.select-scroll {
}
.select-main {
padding: 0 32rpx;
.select-item {
.title {
font-weight: bold;
font-size: 28rpx;
color: #000000;
padding-top: 30rpx;
}
.tag-list {
display: flex;
flex-wrap: wrap;
margin-left: -12rpx;
.tag-item {
margin-top: 20rpx;
padding: 10rpx 40rpx;
font-size: 26rpx;
background: #f5f5f5;
color: #484848;
border-radius: 36rpx;
margin-left: 12rpx;
&.select-on {
background: var(--color);
color: #fff;
}
}
}
}
.select-childer {
.childer-title {
color: #484848;
font-size: 28rpx;
border-bottom: 1px solid #f5f5f5;
height: 80rpx;
}
}
}
.filter-button {
width: 100%;
height: 152rpx;
background: #ffffff;
box-shadow: 0px -3px 12px rgba(0, 0, 0, 0.06);
position: fixed;
bottom: 0;
.btn {
border-radius: 100rpx;
margin-top: 26rpx;
width: 680rpx;
height: 80rpx;
color: #ffffff;
font-size: 28rpx;
overflow: hidden;
.btn-1 {
flex: 1;
background: linear-gradient(271deg, #2698fb 0%, #84c6ff 100%);
}
.btn-2 {
flex: 1;
background: linear-gradient(90deg, #0066ff 0%, #1371ff 100%);
}
}
}
// css
.flex-row {
display: flex;
}
.flex-row-c {
display: flex;
justify-content: center;
}
.flex-row--c {
display: flex;
align-items: center;
}
.flex-row-c-c {
display: flex;
justify-content: center;
align-items: center;
}
.flex-row-sb-c {
display: flex;
justify-content: space-between;
align-items: center;
}
.flex-row-sb-t {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.flex-row-sb-b {
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.flex-row-c-sb {
display: flex;
justify-content: center;
align-items: space-between;
}
.flex-row-sb {
display: flex;
justify-content: space-between;
}
.flex-row-l {
display: flex;
justify-content: flex-start;
}
.flex-row-l-c {
display: flex;
justify-content: flex-start;
align-items: center;
}
.flex-row-c-t {
display: flex;
justify-content: center;
align-items: flex-start;
}
.flex-row-r-c {
display: flex;
justify-content: flex-end;
align-items: center;
}
.flex-row-r {
display: flex;
justify-content: flex-end;
}
.flex-row--b {
display: flex;
align-items: flex-end;
}
.flex-col {
display: flex;
flex-direction: column;
}
.flex-col-c {
display: flex;
flex-direction: column;
justify-content: center;
}
.flex-col--c {
display: flex;
flex-direction: column;
align-items: center;
}
.flex-col-c-c {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.flex-col-sb-c {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.flex-col-c-sb {
display: flex;
flex-direction: column;
justify-content: center;
align-items: space-between;
}
.flex-col-sb {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.flex-col-t-c {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
.flex-col-c-l {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
}
.flex-col-t {
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.flex-col-b {
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.flex-col-b-c {
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
}
.flex-col-c-l {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-end;
}
.flex-col--l {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.flex-col--r {
display: flex;
flex-direction: column;
align-items: flex-end;
}
}
</style>

@ -0,0 +1,140 @@
<template>
<view class="uni-section">
<view class="uni-section-header" nvue>
<view v-if="type" class="uni-section__head">
<view :class="type" class="uni-section__head-tag"/>
</view>
<view class="uni-section__content">
<text :class="{'distraction':!subTitle}" :style="{color:color}" class="uni-section__content-title">{{ title }}</text>
<text v-if="subTitle" class="uni-section__content-sub">{{ subTitle }}</text>
</view>
</view>
<view :style="{padding: padding ? '10px' : ''}">
<slot/>
</view>
</view>
</template>
<script>
/**
* Section 标题栏
* @description 标题栏
* @property {String} type = [line|circle] 标题装饰类型
* @value line 竖线
* @value circle 圆形
* @property {String} title 主标题
* @property {String} subTitle 副标题
*/
export default {
name: 'UniSection',
emits:['click'],
props: {
type: {
type: String,
default: ''
},
title: {
type: String,
default: ''
},
color:{
type: String,
default: '#333'
},
subTitle: {
type: String,
default: ''
},
padding: {
type: Boolean,
default: false
}
},
data() {
return {}
},
watch: {
title(newVal) {
if (uni.report && newVal !== '') {
uni.report('title', newVal)
}
}
},
methods: {
onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss" >
$uni-primary: #2979ff !default;
.uni-section {
background-color: #fff;
// overflow: hidden;
margin-top: 10px;
}
.uni-section-header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding: 12px 10px;
// height: 50px;
font-weight: normal;
}
.uni-section__head {
flex-direction: row;
justify-content: center;
align-items: center;
margin-right: 10px;
}
.line {
height: 12px;
background-color: $uni-primary;
border-radius: 10px;
width: 4px;
}
.circle {
width: 8px;
height: 8px;
border-top-right-radius: 50px;
border-top-left-radius: 50px;
border-bottom-left-radius: 50px;
border-bottom-right-radius: 50px;
background-color: $uni-primary;
}
.uni-section__content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
flex: 1;
color: #333;
}
.uni-section__content-title {
font-size: 14px;
color: $uni-primary;
}
.distraction {
flex-direction: row;
align-items: center;
}
.uni-section__content-sub {
font-size: 12px;
color: #999;
line-height: 16px;
margin-top: 2px;
}
</style>

@ -0,0 +1,10 @@
/**
/**
* 产品 配置文件
* @author yujialong
*/
export default {
normalIcon: 'https://huoran.oss-cn-shenzhen.aliyuncs.com/20220609/png/1534733700683030528.png', // 通用图标
dataIcon: 'https://huoran.oss-cn-shenzhen.aliyuncs.com/20220627/png/1541256164447641600.png' // 数据图标
}

@ -0,0 +1,21 @@
/**
/**
* 请求配置文件
* @author yujialong
*/
export default {
baseURL: 'https://huorantech.cn/',
// baseURL: 'http://192.168.31.137:9000/',
// baseURL: 'http://121.37.12.51/',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
data: {},
method: 'POST',
responseType: 'json', // 响应数据类型
withCredentials: false, // 携带cookie
// ======================== 以下为注入axios的配置项 =============================
showLoading: true, // 是否显示加载动画
isFormData: false // 是否序列化表单数据
}

@ -0,0 +1,16 @@
/**
* @description 鉴权指令
* 当传入的权限当前用户没有时会移除该组件
* 用例<Tag v-auth>text</Tag> <Tag v-auth="'user:'">text</Tag>
* */
export default {
inserted(el, binding, vnode) {
const btnText = el.innerText
const btnPermissions = uni.getStorageSync('auth')
if (btnText && btnPermissions && btnPermissions.length) {
const isPermission = btnPermissions.includes(btnText)
// 如果按钮集合里没有该权限,就把该按钮给去除
!isPermission && el.parentNode && el.parentNode.removeChild(el)
}
},
};

@ -0,0 +1,12 @@
/**
* 插件
* */
import auth from './auth'
export default {
async install(Vue, options) {
// 指令
Vue.directive('auth', auth)
}
};

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

@ -0,0 +1,35 @@
var crypto = require('crypto')
function WXBizDataCrypt(appId, sessionKey) {
this.appId = appId
this.sessionKey = sessionKey
}
WXBizDataCrypt.prototype.decryptData = function (encryptedData, iv) {
// base64 decode
var sessionKey = new Buffer(this.sessionKey, 'base64')
encryptedData = new Buffer(encryptedData, 'base64')
iv = new Buffer(iv, 'base64')
try {
// 解密
var decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv)
// 设置自动 padding 为 true,删除填充补位
decipher.setAutoPadding(true)
var decoded = decipher.update(encryptedData, 'binary', 'utf8')
decoded += decipher.final('utf8')
decoded = JSON.parse(decoded)
} catch (err) {
throw new Error('Illegal Buffer')
}
if (decoded.watermark.appid !== this.appId) {
throw new Error('Illegal Buffer')
}
return decoded
}
module.exports = WXBizDataCrypt

@ -0,0 +1,107 @@
import Product from '@/config/product'
const files = [
'https://huorantech.cn/%E7%94%A8%E6%88%B7%E6%9C%8D%E5%8A%A1%E5%8D%8F%E8%AE%AE.docx', // 用户服务协议
'https://huorantech.cn/%E7%94%A8%E6%88%B7%E9%9A%90%E7%A7%81%E5%8D%8F%E8%AE%AE.docx', // 用户隐私协议
'https://huorantech.cn/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E5%AE%9E%E9%AA%8C%E5%AE%A4%E5%BB%BA%E8%AE%BE%E6%96%B9%E6%A1%88-2020.1.docx', // 人工智能
'https://huorantech.cn/%E5%A4%A7%E6%95%B0%E6%8D%AE%E7%AE%A1%E7%90%86%E4%B8%8E%E5%BA%94%E7%94%A8%E4%B8%93%E4%B8%9A%E5%BB%BA%E8%AE%BE%E6%96%B9%E6%A1%88.docx', // 大数据
'https://huorantech.cn/%E9%87%91%E8%9E%8D%E7%A7%91%E6%8A%80%E5%AE%9E%E9%AA%8C%E5%AE%A4%E5%BB%BA%E8%AE%BE%E6%96%B9%E6%A1%88V2.0.docx', // 金融科技
]
export default {
// 路由跳转
to(url) {
uni.navigateTo({
url
})
},
// 成功提示
sucMsg(title, duration = 1500) {
uni.showToast({
title,
duration
})
},
// 错误提示
errMsg(title, duration = 1500) {
uni.showToast({
title,
icon: 'none',
duration
})
},
// 如果非数字,则返回0
handleNaN(val) {
return isNaN(val) ? 0 : val
},
// 小于10,返回0+传入值
preZero(val) {
return val < 10 ? '0' + val : val
},
//返回格式化时间,传参例如:"yyyy-MM-dd hh:mm:ss"
formatDate(date, fmt = 'yyyy-MM-dd hh:mm:ss') {
var date = date ? date : new Date()
var o = {
"M+" : date.getMonth()+1, //月份
"d+" : date.getDate(), //日
"h+" : date.getHours(), //小时
"m+" : date.getMinutes(), //分
"s+" : date.getSeconds(), //秒
"q+" : Math.floor((date.getMonth()+3)/3), //季度
"S" : date.getMilliseconds() //毫秒
}
if(/(y+)/.test(fmt)) {
fmt=fmt.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length))
}
for(var k in o) {
if(new RegExp("("+ k +")").test(fmt)){
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)))
}
}
return fmt
},
// 获取商务经理id
getBmId(val) {
return uni.getStorageSync('team').partnerId
},
// 获取商务经理名称
getBmName(val) {
return uni.getStorageSync('team').partnerClassificationName
},
// 返回图标。如果有图标,则直接返回,否则判断是否是数据产品,即productType=2,如果是,则取数据图标,否则则显示通用图标,这两个图标都在config/product.js里有配置
getIcon(e) {
return e.miniProgramPictureAddress || (e.productType === 2 ? Product.dataIcon : Product.normalIcon)
},
// 预览文档
openFile(id) {
uni.showLoading({
title: '加载中',
mask: true
})
// 下载文件资源到本地
uni.downloadFile({
url: files[id],
success: function(res) {
uni.hideLoading();
uni.showLoading({
title: '正在打开',
mask: true
})
// 新开页面打开文档,支持格式:doc, xls, ppt, pdf, docx, xlsx, pptx。
uni.openDocument({
filePath: res.tempFilePath,
fileType: 'docx', // 文件类型,指定文件类型打开文件,有效值 doc, xls, ppt, pdf, docx, xlsx, pptx
showMenu: true, // 允许出现分享功能
success: res => {
uni.hideLoading()
},
fail: openError => {
uni.hideLoading()
}
})
},
fail: function(err) {
uni.hideLoading()
}
})
}
}

@ -0,0 +1,39 @@
// #ifndef VUE3
import Vue from 'vue'
import App from './App'
import util from '@/libs/util'
import plugins from '@/directives'
Vue.config.productionTip = false
Vue.prototype.$util = util
Vue.use(plugins)
App.mpType = 'app'
// 权限控制
Vue.prototype.auth = function(text){
const auth = uni.getStorageSync('auth')
if (text && auth && auth.length) {
const isPermission = auth.includes(text)
return auth.includes(text)
}
// return true
}
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
import App from './App.vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif

@ -0,0 +1,60 @@
{
"name" : "或然crm",
"appid" : "__UNI__2E89BA6",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
"app-plus" : {
/* 5+App */
"usingComponents" : true,
"nvueCompiler" : "uni-app",
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
"modules" : {},
/* */
"distribute" : {
/* */
"android" : {
/* android */
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
"ios" : {},
/* ios */
"sdkConfigs" : {}
}
},
/* SDK */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wx88cd6037d54f230a",
"setting" : {
"urlCheck" : false,
"es6" : true,
"minified" : true
},
"usingComponents" : true
},
"vueVersion" : "2"
}

@ -0,0 +1,329 @@
<template>
<view class="page">
<uni-card :is-shadow="false" :border="false" padding="0" is-full>
<uni-search-bar class="search" radius="5" placeholder="请输入产品名称" clearButton="auto" cancelButton="none" v-model="keyword" />
</uni-card>
<ul class="tab">
<li v-for="(tab, i) in tabs" :class="{active: curTab === tab.id}" @click="tabChange(tab)">{{ tab.name }}</li>
</ul>
<ul class="list">
<li v-for="(item, i) in list">
<uni-data-checkbox v-if="item.check" class="check" multiple :value="[1]" :localdata="item.checkData" @change="e => checkChange(e, i)"></uni-data-checkbox>
<uni-data-checkbox v-else class="check" multiple v-model="item.check" :localdata="item.checkData" @change="e => checkChange(e, i)"></uni-data-checkbox>
<image class="icon" :src="$util.getIcon(item)" mode="widthFix"></image>
{{ item.productName }}
</li>
</ul>
<uni-load-more :status="status" />
<view class="btn-wrap">
<uni-data-checkbox class="check" multiple v-model="checkAll" :localdata="checkAllData" @change="allChange"></uni-data-checkbox>
<view class="btn" @click="submit">确定({{ checked.length }})</view>
</view>
</view>
</template>
<script>
import { AppletsDataProductList } from '@/apis/modules/product.js'
import { renew } from '@/apis/modules/order.js'
export default {
data() {
return {
orderType: 1,
customerId: '',
curTab: '',
tabs: [
{
name: '全部',
id: ''
},
{
name: '实训课程',
id: 1
},
{
name: '理论课程',
id: 0
},
{
name: '数据产品',
id: 2
}
],
reachBottom: 0, // 0->,1->,-1->
status: 'more', // more|loading|noMore
searchTimer: null,
orderStatus: '',
productStatus: '',
keyword: '',
list: [],
listAll: [],
page: 1,
pageSize: 10,
check: [1],
noCheck: [],
checkData: [{
text: '',
value: 1
}],
checkAll: [],
checkAllData: [{
text: '全部',
value: 1
}],
checked: uni.getStorageSync('courses') || [], //
courses: uni.getStorageSync('courses') || []
}
},
watch: {
keyword () {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.initList()
}, 500)
}
},
//
onPullDownRefresh() {
this.initList()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1500)
},
//
onReachBottom() {
if (this.reachBottom >= 0) {
this.reachBottom = 1
this.status = 'loading'
this.getList()
}
},
onShow() {
const pages = getCurrentPages()
const { options } = pages[pages.length - 1]
this.orderType = options.orderType
this.customerId = options.customerId
this.getList()
},
methods: {
//
getList() {
uni.showLoading({
title: '加载中'
})
AppletsDataProductList({
sort: 'desc',
keywords: this.keyword,
productType: this.curTab,
pageNum: this.page,
pageSize: this.pageSize
}).then(({ data }) => {
const { records } = data
const list = this.courses
const all = this.checkAll.length //
const pageChange = this.reachBottom > 0 //
const { checked } = this //
//
records.map(e => {
const checkData = {
text: '',
value: 1
}
e.check = (all && pageChange) || checked.find(n => n.id === e.id && n.productType === e.productType) ? 1 : 0
//
if (list.find(n => n.dataOrCourseId == e.id && n.productType == e.productType)) {
//
checkData.disable = true
e.check = 1
}
e.checkData = [checkData]
})
// list
this.list = pageChange ? [...this.list, ...records] : records
this.page++ // page+1
const noMore = this.list.length === data.total //
this.status = noMore ? 'noMore' : 'more' // noMore
this.reachBottom = noMore ? -1 : 0 // -1
uni.hideLoading()
}).catch(e => {
uni.hideLoading()
})
},
initList() {
this.page = 1
this.reachBottom = 0
this.getList()
},
// tab
tabChange(tab) {
this.curTab = tab.id
this.initList()
},
//
checkChange(e, i) {
const { checked } = this
const item = this.list[i]
const { id, productType } = item
const include = checked.findIndex(e => e.id === id && e.productType === productType)
// pushpush
if (e.detail.value.length) {
include === -1 && checked.push(item)
} else {
//
if (include !== -1) {
checked.splice(include, 1)
this.checkAll = []
}
}
},
//
allChange(e) {
const isCheck = !!e.detail.value.length //
const { checked, list } = this
list.map(e => {
e.check = isCheck ? 1 : 0
const { id, productType } = e
const include = checked.findIndex(n => n.id === id && n.productType === productType)
// pushpush
if (isCheck) {
include === -1 && checked.push(e)
} else {
//
include === -1 || checked.splice(include, 1)
}
})
},
//
createParam(e) {
const { orderType } = this
const trial = orderType == 2 //
return {
dataOrCourseId: e.id, // id
productName: e.productName, //
periodOfUse: '', // 使
startTime: this.$util.formatDate(new Date(), 'yyyy-MM-dd'), //
endTime: '', //
remainingPeriod: '', //
marketValue: '', //
marketPrice: e.marketPrice, //
finalPrice: trial ? 0 : '', //
finalValue: trial ? 0 : '', //
discountRate: trial ? '0%' : '', //
accountNum: e.productType === 2 ? '' : 1, //
totalAmount: '', //
isEnable: 1, // 10
ship: 0, // 01
authority: e.productType === 2 ? 0 : 1, // 01
productType: e.productType, // (0-> 1- 2 )
options: 2,
miniProgramPictureAddress: e.miniProgramPictureAddress, //
settlementPrice: trial ? 0 : '', //
settlementMethod: e.settlementMethod, // 01
settlementPriceUnit: e.settlementPrice, //
businessProportion: e.businessProportion, //
serviceFee: 0 //
}
},
//
handleRenew(authority, customerId, productId, result, resolve, reject) {
renew({
authority,
customerId,
productId
}).then(({ orderOthers }) => {
result.map(e => {
const item = orderOthers.find(n => n.dataOrCourseId == e.dataOrCourseId && n.authority == authority && e.authority == authority)
if (item) {
let date = new Date(item.endTime)
date = new Date(date.setDate(date.getDate() + 1))
e.startTime = this.$util.formatDate(date, 'yyyy-MM-dd')
}
})
resolve()
}).catch(e => {
reject()
})
},
//
submit() {
const list = this.checked //
if (list.length) {
const result = this.courses
const courseIds = []
const dataIds = []
const { customerId } = this
list.map(e => {
// id
if (!result.find(n => (n.dataOrCourseId == e.id || n.dataOrCourseId == e.dataOrCourseId) && ((n.authority && e.productType != 2) || (!n.authority && e.productType == 2)))) {
e.productType === 2 ? dataIds.push(e.id) : courseIds.push(e.id)
result.push(this.createParam(e))
}
})
const promises = []
//
dataIds.length && promises.push(new Promise((resolve, reject) => {
this.handleRenew(0, customerId, dataIds, result, resolve, reject)
}))
courseIds.length && promises.push(new Promise((resolve, reject) => {
this.handleRenew(1, customerId, courseIds, result, resolve, reject)
}))
Promise.all(promises).then(_ => {
uni.setStorageSync('courses', result) //
uni.redirectTo({
url: `../editCourse/editCourse?customerId=${customerId}&orderType=${this.orderType}`
})
})
} else {
this.$util.errMsg('请选择产品!')
}
}
}
}
</script>
<style scoped lang="scss">
.page {
padding-bottom: 130rpx;
}
.list {
li {
display: flex;
align-items: center;
padding: 30rpx 24rpx;
margin: 16rpx 24rpx;
font-size: 30rpx;
color: #333;
background-color: #fff;
border-radius: 16rpx;
}
.icon {
width: 80rpx;
margin: 0 20rpx;
}
}
/deep/.check {
.checklist-box {
margin: 0 !important;
}
.checkbox__inner {
width: 40rpx !important;
height: 40rpx !important;
border-radius: 50% !important;
}
.checkbox__inner-icon {
top: 8rpx !important;
left: 14rpx !important;
}
}
.btn-wrap {
position: fixed;
justify-content: space-between;
.btn {
width: 340rpx;
margin-left: 27rpx;
}
}
</style>

@ -0,0 +1,266 @@
<template>
<view>
<view class="filter">
<uni-search-bar class="search" radius="30" placeholder="请输入订单号、内容" v-model="keyword" clearButton="auto" cancelButton="none" />
<uni-icons class="icon" custom-prefix="iconfont" type="icon-filter" size="22" color="#007eff" @click="popup = true"></uni-icons>
</view>
<template v-if="list.length">
<view class="list">
<uni-swipe-action>
<uni-swipe-action-item
v-for="item in list"
:threshold="0"
:right-options="delOption"
@click="del(item)"
>
<view class="item" @click="toDetail(item)">
<view class="c-name">{{ item.orderNumber }}</view>
<view class="info">
<view class="left">
<view v-if="curTab" class="line">
<text class="name">商务经理</text>
<text class="val">{{ item.businessManagerName }}</text>
</view>
<view class="line">
<text class="name">客户名称</text>
<text class="val">{{ item.customerName }}</text>
</view>
<view class="line">
<text class="name">订单金额</text>
<text class="val">{{ item.orderAmount }}</text>
</view>
<view class="line">
<text class="name">订单内容</text>
<view class="val ell-wrap">
<view :class="{ell: !item.toggle}">{{ item.productName }}</view>
<view v-if="item.productName.length > 14" class="toggle" @click.stop="toggle(item)">{{ item.toggle ? '收起' : '展开' }}</view>
</view>
</view>
<view class="line">
<text class="name">下单日期</text>
<text class="val">{{ item.createTime }}</text>
</view>
</view>
<view :class="['type', 'type' + item.orderStatus]">
{{ filterData[0].data.find(e => e.value === item.orderStatus).title }}
</view>
</view>
</view>
</uni-swipe-action-item>
</uni-swipe-action>
</view>
<uni-load-more :status="status" />
</template>
<empty v-else></empty>
<uni-icons class="plus" type="plus-filled" size="60" color="#007eff" @click="$util.to(`../orderDetail/orderDetail?customerId=${customerId}`)"></uni-icons>
<filter-popup :data="filterData" :form.sync="filterForm" v-model="popup" title="全部筛选" height="1104rpx" @finsh="subFinsh"></filter-popup>
</view>
</template>
<script>
import { miniProgramOrderRecord, del } from '@/apis/modules/order.js'
export default {
data() {
return {
customerId: '',
customerName: '',
popup: false,
//
filterData: [
{
children: false,//
title: "订单状态",
key: "orderStatus", //
keyValue: "value", //
isRadio: true, //
data: [
{
title: "待发货",
value: 0
},
{
title: "已完成",
value: 1
},
],
}
],
reachBottom: 0, // 0->,1->,-1->
status: 'more', // more|loading|noMore
filterForm: {},
searchTimer: null,
orderStatus: '',
keyword: '',
list: [],
page: 1,
pageSize: 10,
delOption: [{
text: '删除',
style: {
backgroundColor: '#F56C6C'
}
}]
}
},
watch: {
keyword () {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.initList()
}, 500)
}
},
//
onReachBottom() {
if (this.reachBottom >= 0) {
this.reachBottom = 1
this.status = 'loading'
this.getList()
}
},
//
onPullDownRefresh() {
this.getList()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1500)
},
onShow() {
const pages = getCurrentPages()
const { options } = pages[pages.length - 1]
this.customerId = options.customerId
this.customerName = options.name
this.initList()
},
methods: {
getList() {
uni.showLoading({
title: '加载中'
})
const team = uni.getStorageSync('team')
const { orderStatus } = this
miniProgramOrderRecord({
businessManagerId: this.$util.getBmId(),
teamId: team.id,
customerId: +this.customerId,
orderStatus: orderStatus === '' ? null : orderStatus,
isAdmin: +team.isTeam, // (0 1)
pageNum: this.page,
pageSize: this.pageSize,
keywords: this.keyword
}).then(({ data }) => {
const { records } = data
records.map(e => {
e.toggle = e.productName.length < 14 // 14
})
this.list = this.reachBottom > 0 ? [...this.list, ...records] : records
this.page++ // page+1
const noMore = this.list.length === data.total //
this.status = noMore ? 'noMore' : 'more' // noMore
this.reachBottom = noMore ? -1 : 0 // -1
uni.hideLoading()
}).catch(e => {
uni.hideLoading()
})
},
initList() {
this.page = 1
this.reachBottom = 0
this.getList()
},
//
toggle(item) {
item.toggle = !item.toggle
},
//
subFinsh(val) {
const { orderStatus } = val
this.orderStatus = orderStatus.length ? orderStatus[0] : ''
this.initList()
},
//
toDetail(item) {
this.$util.to(`../orderDetail/orderDetail?orderId=${item.orderId}&show=1`)
},
//
del(e) {
const that = this
uni.showModal({
title: '提示',
content: '确定要删除吗?',
success(res) {
if (res.confirm) {
del({
ids: [e.orderId]
}).then(res => {
that.$util.sucMsg('删除成功')
that.getList()
}).catch(res => {})
}
}
})
},
}
}
</script>
<style scoped lang="scss">
.list {
margin-top: 20rpx;
background-color: #fff;
.item {
padding: 20rpx 40rpx;
border-bottom: 1px solid #f1f1f1;
}
.c-name {
font-size: 30rpx;
color: #333;
}
.info {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10rpx;
}
.left {
max-width: 70%;
}
.line {
display: flex;
padding: 10rpx 0;
}
.name {
margin-right: 10rpx;
white-space: nowrap;
font-size: 28rpx;
color: #999;
}
.val {
max-width: 88%;
font-size: 28rpx;
color: #333;
}
.ell {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.toggle {
margin-top: 10rpx;
white-space: nowrap;
font-size: 24rpx;
color: #0e92ef;
}
.type {
margin-left: 20rpx;
font-size: 28rpx;
color: #ff7b2d;
white-space: nowrap;
}
.type1 {
color: #bdbdbd;
}
}
</style>

@ -0,0 +1,420 @@
<template>
<view class="page">
<template v-for="c in courses">
<view v-if="c.list.length" class="block">
<view class="l-title">{{ c.name }}</view>
<uni-icons class="arrow" type="top" size="20" color="#007EFF" @click="toggle(c)"></uni-icons>
<view v-show="!c.shrink">
<view v-for="(item, i) in c.list">
<view class="pro-name">
<view class="left">
<image class="icon" :src="$util.getIcon(item)" mode="widthFix"></image>
{{ item.productName }}
</view>
<uni-icons class="del" type="trash" size="25" color="#ADADAD" @click="delCourse(c, i)"></uni-icons>
</view>
<view class="form-list">
<view class="line">
<view class="name">产品类型</view>
<view class="val">{{ productTypes.find(e => e.id === item.productType).name }}</view>
</view>
<view :class="['line req', {err: err === 'periodOfUse' + item.dataOrCourseId + item.authority}]">
<view class="name">使用期限</view>
<input class="period" type="number" v-model="item.periodOfUse" placeholder="请输入" @input="calcDate(item, !item.authority)" @change="handleErr(item, 'periodOfUse')">
<view class="val unit" @click="selectUnit(item)">
<text>{{ units.find(e => e.id === item.options).text }}</text>
<image class="icon" src="@/static/image/arrow-down.png" mode="widthFix"></image>
</view>
</view>
<view :class="['line req', {err: err === 'startTime' + item.dataOrCourseId + item.authority}]">
<view class="name">起止日期</view>
<uni-datetime-picker type="date" v-model="item.startTime" :border="false" @change="calcDate(item)">
<view :class="['ph', {val: item.startTime}]">
{{ item.endTime ? item.startTime + ' - ' + item.endTime : item.startTime}}
</view>
</uni-datetime-picker>
</view>
<view :class="['line req', {err: err === 'accountNum' + item.dataOrCourseId + item.authority}]">
<view class="name">数量</view>
<view v-if="item.authority" class="val">1</view>
<input v-else type="number" v-model="item.accountNum" placeholder="请输入账号数量" @input="calcFinalPrice(item)" @change="handleErr(item, 'accountNum')">
</view>
<view class="line">
<view class="name">{{ item.authority ? '市场价' : '市场单价' }}</view>
<view class="val">{{ item.marketValue }}</view>
</view>
<view class="line">
<view class="name">结算价</view>
<view class="val">{{ item.settlementPrice }}</view>
</view>
<view class="line">
<view class="name">折扣率</view>
<view class="val">{{ item.discountRate }}</view>
</view>
<view class="line">
<view class="name">平台服务费</view>
<view class="val">{{ item.serviceFee }}</view>
</view>
<view :class="['line req', {err: err === 'finalPrice' + item.dataOrCourseId + item.authority}]">
<view class="name">成交价</view>
<view class="inline">
<input type="number" v-model="item.finalPrice" placeholder="请输入" @input="calcFinalValue(item)" @change="handleErr(item, 'finalPrice')">
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<view class="btn-wrap">
<view class="btn" @click="submit">确定</view>
</view>
</view>
</template>
<script>
import { getOrderOtherTime } from '@/apis/modules/order.js'
export default {
data() {
return {
orderType: 1,
customerId: '',
timer: null,
units: [{
text: '日',
id: 0
}, {
text: '月',
id: 1
}, {
text: '年',
id: 2
}],
unitText: ['日', '月', '年'],
productTypes: [
{
name: '实训课程',
id: 1
},
{
name: '理论课程',
id: 0
},
{
name: '数据产品',
id: 2
}
],
courses: {} , //
orderRepeat: [],
repeatMsg: '',
err: ''
}
},
onShow() {
const pages = getCurrentPages()
const { options } = pages[pages.length - 1]
this.orderType = options.orderType
this.customerId = options.customerId
this.handleProduct()
},
methods: {
//
handleProduct() {
const list = uni.getStorageSync('courses')
const courses = {
practice: {
shrink: false,
name: '实训课程产品',
list: []
},
theory: {
shrink: false,
name: '理论课程产品',
list: []
},
data: {
shrink: false,
name: '数据产品',
list: []
}
}
// 3push(0-> 1- 2 )
list.map(e => {
const type = e.productType
!type ?
courses.theory.list.push(e) :
type === 1 ?
courses.practice.list.push(e) :
courses.data.list.push(e)
})
this.courses = courses
try {
uni.removeStorageSync('courses')
} catch (e) {}
},
selectUnit(item) {
const that = this
uni.showActionSheet({
title: '标题',
itemList: that.unitText,
success: ({ tapIndex }) => {
item.options = tapIndex
that.calcDate(item)
}
})
},
showUnit(i) {
this.$refs.unit[i].show()
},
//
toggle(c) {
c.shrink = !c.shrink
},
//
delCourse(c, i) {
uni.showModal({
title: '提示',
content: '确定要删除吗?',
success(res) {
res.confirm && c.list.splice(i, 1)
}
})
},
// 使
calcDate(row, fromData) {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
const { periodOfUse, options } = row
let optionsData = 0
if (periodOfUse) {
if (options == 1){
optionsData = periodOfUse === '12' ? 31536000000 : periodOfUse*30*24*60*60*1000
} else if (options == 2){
optionsData = periodOfUse*365*24*60*60*1000
} else {
optionsData = periodOfUse*24*60*60*1000
}
}
let time = new Date(row.startTime).getTime()
let endTime = time + optionsData
row.endTime = time + optionsData
let dt = new Date(endTime)
row.endTime = (dt.getFullYear()) + "-" + (dt.getMonth() + 1) + "-" + (dt.getDate())
let endYear = endTime - time
let endYears = endYear/1000/60/60/24
row.remainingPeriod = endYears
const unit = row.options // 使
const useUnit = row.periodOfUse // 使
//
const price = row.marketPrice //
// //365,/12)
row.marketValue = (!unit ?
price / 365 * useUnit :
unit === 1 ?
price / 12 * useUnit :
price * useUnit).toFixed(2)
this.dealSettlePrice(row)
// +1
if (!fromData) {
const cId = row.dataOrCourseId
const date = new Date(row.startTime)
const orderRepeat = this.orderRepeat
getOrderOtherTime({
customerId: this.customerId,
id: cId,
startTime: this.$util.formatDate(date, 'yyyy-MM-dd'),
endTime: row.endTime
}).then(res => {
orderRepeat.includes(cId) && orderRepeat.splice(orderRepeat.findIndex(e => e === cId), 1)
if (res.endTime) {
let time = new Date(res.endTime)
time = new Date(time.setDate(time.getDate() + 1))
row.startTime = this.$util.formatDate(time, 'yyyy-MM-dd')
}
}).catch(res => {
this.repeatMsg = res.message
orderRepeat.includes(cId) || orderRepeat.push(cId)
})
}
//
this.calcDiscount(row)
}, 500)
},
//
dealSettlePrice(row) {
// 0
if (this.orderType == 2) {
row.settlementPrice = 0
row.serviceFee = 0
} else {
const unit = row.options // 使
const useUnit = row.periodOfUse // 使
let sPrice = ''
if (row.settlementMethod == 0) {
// **/**(1)
const priceUnit = row.settlementPriceUnit
sPrice = ((!unit ?
priceUnit / 365 * useUnit :
unit === 1 ?
priceUnit / 12 * useUnit :
priceUnit * useUnit) * (row.authority ?
1 :
row.accountNum)).toFixed((2))
} else {
// *
sPrice = (row.finalPrice * row.businessProportion / 100).toFixed((2))
}
row.settlementPrice = this.$util.handleNaN(sPrice)
// *10%
if (row.settlementPrice) {
row.serviceFee = (row.settlementPrice * 0.1).toFixed(2)
}
}
},
//
calcDiscount(row) {
const price = row.authority ? row.finalPrice : row.finalValue
const { marketValue } = row
// (-)÷ x100%
if (price) row.discountRate = marketValue != 0 ? ((marketValue - price) / marketValue * 100).toFixed(2) + '%' : '0%'
},
// ////
calcFinalValue(row) {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
const { authority, periodOfUse, options, accountNum, finalPrice } = row
if (!authority && periodOfUse && accountNum && finalPrice) {
row.finalValue = (finalPrice / accountNum / periodOfUse).toFixed(2)
}
//
this.calcDiscount(row)
this.dealSettlePrice(row)
}, 500)
},
// **//
calcFinalPrice(row) {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
const { finalValue, accountNum, periodOfUse, finalPrice } = row
if (periodOfUse) {
if (accountNum) {
// =**
if (finalValue) {
row.finalPrice = Math.round(finalValue * periodOfUse * accountNum)
} else if (!finalValue && finalPrice) {
// =//
row.finalValue = (finalPrice / periodOfUse / accountNum).toFixed(2)
this.calcDiscount(row)
}
} else if (finalValue && finalPrice && !row.authority) {
// =//
row.accountNum = Math.floor(finalPrice / periodOfUse / finalValue)
}
}
this.dealSettlePrice(row)
}, 500)
},
//
handleErr(e, val) {
if (val + e.dataOrCourseId + e.authority === this.err) this.err = ''
},
//
submit() {
const courses = this.courses
const list = []
let msg = ''
// push便
for (const i in courses) {
list.push(...courses[i].list)
}
console.log(11, list)
//
for (const i in list) {
const e = list[i]
const suf = e.dataOrCourseId + '' + e.authority
if (e.periodOfUse === '') {
this.err = 'periodOfUse' + suf
msg = '请输入使用期限!'
break
}
if (!e.startTime) {
this.err = 'startTime' + suf
msg = '请选择起止日期!'
break
}
if (e.accountNum === '') {
this.err = 'accountNum' + suf
msg = '请输入数量!'
break
}
if (e.finalPrice === '') {
this.err = 'finalPrice' + suf
msg = '请输入成交价!'
break
}
}
console.log(33, this.err)
if (msg) return this.$util.errMsg(msg)
if (this.orderRepeat.length) return this.$util.errMsg(this.repeatMsg) //
uni.setStorageSync('courses', this.courses)
uni.navigateBack()
},
}
}
</script>
<style scoped lang="scss">
.page {
padding-bottom: 130rpx;
-webkit-overflow-scrolling: touch;
}
.block {
position: relative;
padding: 0;
.l-title {
margin: 0 24rpx;
}
}
.arrow {
position: absolute;
top: 30rpx;
right: 30rpx;
}
.pro-name {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14rpx 24rpx;
font-size: 30rpx;
color: #333;
background: linear-gradient(90deg, #FFF5E5 0%, #FFFFFF 100%);
.left {
display: inline-flex;
align-items: center;
}
.icon {
width: 60rpx;
height: 60rpx;
margin-right: 12rpx;
border-radius: 10rpx;
}
}
.form-list {
padding: 0 24rpx;
border-top: 0;
.period {
text-align: center;
}
.unit {
display: inline-flex;
align-items: center;
.icon {
width: 28rpx;
margin-left: 20rpx;
}
}
}
</style>

@ -0,0 +1,644 @@
<template>
<view :class="['page', { show: isDetail }]">
<view class="block">
<view class="l-title">基本信息</view>
<view class="form-list">
<view class="line">
<view class="name">商务经理</view>
<view class="val">{{ form.businessManagerName }}</view>
</view>
<view :class="['line req', {err: err === 'customerName'}]">
<view class="name">客户名称</view>
<view v-if="isDetail" class="val">{{ form.customerName }}</view>
<view v-else :class="['ph', {val: form.customerName}]" @click="customerVisible = true">{{ form.customerName || '请选择客户' }}</view>
</view>
<view class="line">
<view class="name">省份</view>
<view class="val">{{ form.provinceName }}</view>
</view>
<view class="line">
<view class="name">城市</view>
<view class="val">{{ form.cityName }}</view>
</view>
<view class="line">
<view class="name">联系人</view>
<view class="val">{{ form.orderContact }}</view>
</view>
<view class="line">
<view class="name">电话</view>
<view class="val">{{ form.phone }}</view>
</view>
<view class="line">
<view class="name">邮箱</view>
<view class="val">{{ form.email }}</view>
</view>
<view :class="['line req', {err: err === 'orderType'}]">
<view class="name">订单类型</view>
<view v-if="isDetail" class="val">{{ orderTypes.find(e => e.value === form.orderType).text }}</view>
<uni-data-picker v-else class="picker-input" placeholder="请选择订单类型" popup-title="请选择订单类型" preload :clear-icon="false" :localdata="orderTypes" v-model="form.orderType" @change="calcTotal(1)"></uni-data-picker>
</view>
<view class="line">
<view class="name">订单编号</view>
<view class="val">{{ form.orderNumber }}</view>
</view>
<view class="line">
<view class="name">订单时间</view>
<view class="val">{{ form.createTime }}</view>
</view>
<view class="line">
<view class="name">订单金额</view>
<view class="val">{{ form.orderAmount }}</view>
</view>
</view>
</view>
<template v-for="c in courseList">
<view v-if="c.list.length" class="block pro-wrap" id="products">
<view class="l-title">{{ c.name }}</view>
<uni-icons class="arrow" type="top" size="20" color="#007EFF" @click="toggle(c)"></uni-icons>
<ul class="pro-list" v-show="!c.shrink">
<li v-for="(item, i) in c.list" @click.stop="editCourse(c, i)">
<view class="name">
<view class="left">
<image class="icon" :src="$util.getIcon(item)" mode="widthFix"></image>
{{ item.productName }}
</view>
<image v-if="!isDetail" class="del" src="@/static/image/trash.png" mode="widthFix" @click.stop="delCourse(c, i)"></image>
</view>
<view class="info">
<view class="line">
<view class="label">使用期限</view>
<view class="val">{{ item.startTime + ' - ' + item.endTime }}</view>
</view>
<view class="line">
<view class="label">市场价</view>
<view class="val">{{ item.marketValue }}</view>
</view>
<view class="line">
<view class="label">结算价</view>
<view class="val">{{ item.settlementPrice }}</view>
</view>
<view class="line">
<view class="label">折扣率</view>
<view class="val">{{ item.discountRate }}</view>
</view>
<view class="line">
<view class="label">平台服务费</view>
<view class="val">{{ item.serviceFee }}</view>
</view>
<view class="line done">
<view class="val">成交价格</view>
<view class="price">{{ item.finalPrice }}</view>
</view>
</view>
</li>
</ul>
</view>
</template>
<view class="bottom">
<view v-if="!orderId" class="add-wrap">
<view class="add-btn" @click="toAdd">
<uni-icons class="icon" type="plus" color="#007FFF"></uni-icons>
添加产品
</view>
</view>
<view :class="['product', isDetail ? 'show' : 'edit']">
<view class="text">
<view class="total">
<view v-if="isDetail">共选<text class="num">{{ courses.length }}</text>个产品</view>
<view class="column">总成交价合计<text class="num">{{ form.orderAmount }}</text></view>
</view>
<view class="info">
<view class="line">
总采购成本:<text class="num">{{ form.purchaseCost }}</text>
</view>
<view class="line">
总产品利润:<text class="num"> {{ form.profit }}</text>
</view>
</view>
</view>
<view v-if="!isDetail" class="btn" @click="submit">提交({{ courses.length }})</view>
</view>
</view>
<view class="popup-mask" v-show="customerVisible" @click="closeCustomer"></view>
<view class="popup" v-show="customerVisible">
<view class="top">请选择客户</view>
<uni-icons class="close" type="closeempty" size="20" @click="closeCustomer"></uni-icons>
<uni-search-bar class="search" radius="5" placeholder="请输入客户名称" v-model="keyword" clearButton="auto" cancelButton="none" />
<view class="list">
<view class="item" v-for="item in customerList" @click="customerChange(item)">{{ item.customerName }}</view>
</view>
</view>
</view>
</template>
<script>
import { queryCustomer, queryCustomerDetails } from '@/apis/modules/client.js'
import { add, update, getDetail } from '@/apis/modules/order.js'
export default {
data() {
return {
isDetail: false,
orderId: '',
orderTypes: [{
text: '正式',
value: 1
}, {
text: '试用',
value: 2
}],
form: {
businessManagerName: uni.getStorageSync('team').userName,
// orderNumber: '', //
provinceId: '', // id
provinceName: '',
orderContact: '', //
email: '',
customerId: '', // id
customerName: '',
orderType: '',
cityId: '',
cityName: '',
phone: '',
orderAmount: 0,
customerName: '',
profit: 0, //
purchaseCost: 0, //
teamId: uni.getStorageSync('team').id,
businessManagerId: this.$util.getBmId()
},
courseList: {},
courses: [], //
contract: { //
contractName: '', //
contractFile: '', //
contractMoney: '', //
contractNumber: '',//
startTime: '', // -
endTime: '' // -
},
customerVisible: false,
keyword: '',
searchTimer: null,
customerList: [],
customerListAll: [],
err: ''
}
},
watch: {
keyword () {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.filterCustomer()
}, 500)
}
},
onShow() {
const pages = getCurrentPages()
const { options } = pages[pages.length - 1]
this.orderId = options.orderId
this.isDetail = !!options.show
const store = uni.getStorageSync('courses')
if (this.orderId) {
this.getInfo()
} else if (store) {
//
const list = []
for (const i in store) {
list.push(...store[i].list)
}
this.courseList = store
this.courses = list
this.$nextTick(() => {
uni.pageScrollTo({
selector: '#products',
})
})
try {
uni.removeStorageSync('courses')
} catch (e) {}
this.calcTotal()
} else {
// id
const { customerId } = options
customerId && this.customerChange({
customerId
})
}
//
if (!this.isDetail) {
this.getCustomer()
}
//
uni.setNavigationBarTitle({
title: this.orderId ?
(this.isDetail ?
'订单详情' :
'编辑订单') :
'新增订单'
})
},
methods: {
//
getInfo() {
uni.showLoading({
title: '加载中'
})
getDetail({
orderId: this.orderId
}).then(({ orderDetails }) => {
const order = orderDetails
this.contract = order.contractInformation
this.form = order.order
this.courses = order.orderOther
this.handleProduct(order.orderOther)
this.calcTotal()
uni.hideLoading()
}).catch(e => {
uni.hideLoading()
})
},
//
handleProduct(list) {
const courses = {
practice: {
shrink: false,
name: '实训课程产品',
list: []
},
theory: {
shrink: false,
name: '理论课程产品',
list: []
},
data: {
shrink: false,
name: '数据产品',
list: []
}
}
// 3push(0-> 1- 2 )
list.map(e => {
e.serviceFee = (e.settlementPrice * 0.1).toFixed(2)
const type = e.productType
!type ?
courses.theory.list.push(e) :
type === 1 ?
courses.practice.list.push(e) :
courses.data.list.push(e)
})
this.courseList = courses
},
//
getCustomer() {
queryCustomer({
countries: '中国',
provinceId: '',
cityId: '',
searchContent: this.keyword,
page: 1,
size: 10000
}).then(({ message }) => {
this.customerListAll = message.list
this.customerList = message.list
}).catch(res => {})
},
//
filterCustomer() {
const { keyword } = this
this.customerList = keyword ?
this.customerListAll.filter(e => e.customerName.includes(keyword)) :
this.customerListAll
},
//
closeCustomer() {
this.customerVisible = false
this.keyword = ''
},
//
customerChange(item) {
const { form } = this
//
queryCustomerDetails({
customerId: item.customerId
}).then(({ result }) => {
this.handleErr('customerName')
const e = result.customer
form.customerId = e.customerId
form.customerName = e.customerName
form.provinceId = e.provinceId
form.cityId = e.cityId
form.provinceName = e.provinceName
form.cityName = e.cityName
form.phone = e.phone
form.email = e.email
form.orderContact = e.name
}).catch(e => {})
this.closeCustomer()
},
//
toAdd() {
uni.setStorageSync('courses', this.courses)
const { customerId, orderType } = this.form
customerId ?
this.$util.to(`../addCourse/addCourse?orderType=${orderType}&customerId=${customerId}`) :
this.$util.errMsg('请先选择客户!')
},
//
editCourse(c, i) {
if (this.isDetail) return false
uni.setStorageSync('courses', this.courses)
const { customerId, orderType } = this.form
this.$util.to(`../editCourse/editCourse?orderType=${orderType}&customerId=${customerId}`)
},
//
delCourse(c, i) {
const that = this
uni.showModal({
title: '提示',
content: '确定要删除吗?',
success(res) {
if (res.confirm) {
c.list.splice(i, 1)
const courses = []
const list = that.courseList
for (const j in list) {
list[j].list.map(n => {
courses.push(n)
})
}
that.courses = courses
that.calcTotal()
}
}
})
},
//
toggle(c) {
c.shrink = !c.shrink
},
//
calcTotal(out) {
this.handleErr('orderType')
const { form } = this
const isTrial = form.orderType === 2 //
let total = 0
const list = this.courses
let purchase = 0 //
let profit = 0 //
/**
* 1总采购成本+总产品利润=总成交价格
2单个产品采购成本 =结算价+平台服务费
3平台服务费=结算价*10%
4单个产品的利润=成交价-采购成本
总成本和总利润为单个产品的采购成本价和利润的和
*/
list.map(e => {
// out=1 && 0
if (out && isTrial) {
e.settlementPrice = 0
e.serviceFee = 0
e.finalPrice = 0
e.finalValue = 0
e.discountRate = '0%'
} else {
const curPurchase = +e.settlementPrice + +e.serviceFee
purchase += curPurchase
profit += +e.finalPrice - curPurchase
}
})
form.purchaseCost = Math.round(purchase)
form.profit = Math.round(profit)
form.orderAmount = +form.purchaseCost + +form.profit
},
//
handleErr(val) {
if (val === this.err) this.err = ''
},
//
submit() {
const { form, courses } = this
if (!form.customerId) {
this.err = 'customerName'
return this.$util.errMsg('请选择客户!')
}
if (!form.orderType) {
this.err = 'orderType'
return this.$util.errMsg('请选择订单类型!')
}
if (!courses.length) return this.$util.errMsg('请选择课程权限或数据权限后再确认订单!')
courses.map(e => {
e.startTime = this.$util.formatDate(new Date(e.startTime), 'yyyy-MM-dd')
})
uni.showLoading({
title: '提交中'
})
const data = {
contractInformation: this.contract, //
order: form, //
orderOther: courses //
}
if (this.orderId) {
update(data).then(res => {
uni.hideLoading()
this.$util.sucMsg('编辑成功')
setTimeout(() => {
uni.navigateBack()
}, 1500)
}).catch(res => {
uni.hideLoading()
})
} else {
add(data).then(res => {
uni.hideLoading()
this.$util.sucMsg('添加成功')
setTimeout(() => {
uni.navigateBack()
}, 1500)
}).catch(res => {
uni.hideLoading()
})
}
}
}
}
</script>
<style scoped lang="scss">
.page {
padding-bottom: 420rpx;
}
.show {
padding-bottom: 280rpx;
}
.pro-wrap {
position: relative;
padding: 0;
.l-title {
margin: 0 24rpx;
}
}
.arrow {
position: absolute;
top: 20rpx;
right: 30rpx;
}
.pro-list {
.name {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14rpx 24rpx;
font-size: 30rpx;
color: #333;
background: linear-gradient(90deg, #FFF5E5 0%, #FFFFFF 100%);
}
.left {
display: inline-flex;
align-items: center;
}
.del {
width: 40rpx;
}
.icon {
width: 60rpx;
height: 60rpx;
margin-right: 12rpx;
border-radius: 10rpx;
}
.info {
padding: 12rpx 24rpx;
}
.line {
display: flex;
justify-content: space-between;
padding: 12rpx 0;
margin: 5px 0;
font-size: 12px;
}
.label {
font-size: 28rpx;
color: #999;
}
.val {
font-size: 28rpx;
color: #333;
}
.done {
margin-top: 12rpx;
padding-top: 24rpx;
font-size: 13px;
color: #333;
border-top: 1px solid #E6E8ED;
}
.price {
font-size: 32rpx;
color: #007EFF;
}
}
.bottom {
position: fixed;
bottom: 0;
padding-bottom: env(safe-area-inset-bottom);
width: 100%;
background-color: #fff;
box-shadow: 0 -2px 2px #f5f5f5;
box-sizing: border-box;
}
.add-wrap {
padding: 16rpx 24rpx 40rpx;
background-color: #f5f5f5;
}
/deep/.add-btn {
display: flex;
justify-content: center;
align-items: center;
font-size: 28rpx;
line-height: 88rpx;
color: #007EFF;
background-color: #fff;
border: 1px solid #007EFF;
border-radius: 16rpx;
.icon {
margin-right: 10rpx;
}
.uni-icons {
font-size: 38rpx !important;
}
}
.product {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
background-color: #fff;
.total {
white-space: nowrap;
}
.line {
&:first-child {
margin-bottom: 8px;
}
}
&.show {
padding: 24rpx 50rpx;
.total {
display: flex;
margin-bottom: 30rpx;
font-size: 30rpx;
font-weight: 600;
color: #333;
.column {
margin-left: 10rpx;
}
.num {
margin: 0 5rpx;
font-size: 34rpx;
font-weight: 600;
color: #007EFF;
}
}
}
&.edit {
.total {
margin-bottom: 10rpx;
text-align: right;
font-size: 30rpx;
color: #666;
.num {
font-weight: 600;
font-size: 34rpx;
}
}
.num {
margin: 0 5rpx;
font-size: 30rpx;
color: #007EFF;
}
}
.info {
font-size: 24rpx;
color: #333;
white-space: nowrap;
}
.btn {
width: 180rpx;
margin-left: 30rpx;
line-height: 80rpx;
font-size: 32rpx;
text-align: center;
color: #fff;
border-radius: 10rpx;
background-color: #007EFF;
}
}
</style>

@ -0,0 +1,270 @@
<template>
<view>
<view class="filter">
<uni-search-bar class="search" radius="30" placeholder="请输入产品名称" v-model="keyword" clearButton="auto" cancelButton="none" />
<uni-icons class="icon" custom-prefix="iconfont" type="icon-filter" size="22" color="#007eff" @click="popup = true"></uni-icons>
</view>
<ul class="tab">
<li v-for="(tab, i) in tabs" :class="{active: curTab === tab.id}" @click="tabChange(tab)">{{ tab.name }}</li>
</ul>
<ul v-if="list.length" class="list">
<li v-for="item in list">
<view class="pro-name">
<image class="icon" :src="item.miniProgramPictureAddress ? item.miniProgramPictureAddress : normalIcon" mode="widthFix"></image>
{{ item.productName }}
</view>
<view class="info">
<view class="line">
<text class="name">起止日期</text>
<text class="val">{{ item.startAndEndTime }}</text>
</view>
<view class="line">
<text class="name">订阅状态</text>
<text class="val">{{ item.status }}</text>
</view>
<view class="line">
<text class="name">产品状态</text>
<text class="val">{{ item.isEnable }}</text>
</view>
</view>
</li>
</ul>
<empty v-else></empty>
<filter-popup :data="filterData" :form.sync="filterForm" v-model="popup" title="全部筛选" height="1104rpx" @finsh="subFinsh"></filter-popup>
</view>
</template>
<script>
import { getProductsSubscribedByCustomers } from '@/apis/modules/client.js'
import product from '@/config/product.js'
export default {
data() {
return {
normalIcon: product.normalIcon,
customerId: '',
popup: false,
//
filterData: [
{
children: false,//
title: "订阅状态",
key: "orderStatus", //
keyValue: "value", //
isRadio: true, //
data: [
{
title: '生效',
value: 1
},
{
title: '过期',
value: 2
},
],
},
{
children: false,//
title: "产品状态",
key: "productStatus", //
keyValue: "value", //
isRadio: true, //
data: [
{
title: '启用',
value: 1
},
{
title: '禁用',
value: 2
},
],
}
],
filterForm: {},
curTab: '',
tabs: [
{
name: '全部',
id: ''
},
{
name: '实训课程',
id: 1
},
{
name: '理论课程',
id: 0
},
{
name: '数据产品',
id: 3
}
],
searchTimer: null,
orderStatus: '',
productStatus: '',
keyword: '',
list: [],
listAll: [],
page: 1,
pageSize: 10
}
},
watch: {
keyword () {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.filter()
}, 500)
}
},
//
onPullDownRefresh() {
this.getList()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1500)
},
onShow() {
const pages = getCurrentPages()
this.customerId = pages[pages.length - 1].options.customerId
this.getList()
},
methods: {
getList() {
uni.showLoading({
title: '加载中'
})
getProductsSubscribedByCustomers({
customeId: this.customerId
}).then(({ data }) => {
const { tabs } = this
data.map(e => {
const list = e.startAndEndTimeList
let connect = true //
list.map((n, i) => {
//
if (i) {
if (new Date(n.startTime).getTime() - 86400000 !== new Date(list[i - 1].endTime).getTime()) connect = false
}
})
//
const now = Date.now()
if (now < list[0].startTime) {
e.startTime = list[0].startTime
e.endTime = connect ? list[list.length - 1].endTime : list[0].endTime
e.status = '未生效'
} else if (now > list[list.length - 1].endTime) {
e.status = '已过期'
} else {
//
if (connect) {
e.startTime = list[0].startTime
e.endTime = list[list.length - 1].endTime
e.status = '生效中'
e.orderEnable = list[0].isEnable
} else {
for (const i in list) {
const n = list[i]
if (now >= new Date(n.startTime).getTime() && now <= new Date(n.endTime).getTime()) {
//
e.startTime = n.startTime
e.endTime = n.endTime
e.status = '生效中'
e.orderEnable = n.isEnable
break
} else if (i && now > new Date(list[i - 1].endTime).getTime() && now < new Date(n.startTime).getTime()) {
//
e.startTime = n.startTime
e.endTime = n.endTime
e.status = '未生效'
e.orderEnable = n.isEnable
break
}
}
}
}
const date = new Date()
date.setHours(0)
date.setMinutes(0)
date.setSeconds(0)
e.startAndEndTime = e.startTime + ' ~ ' + e.endTime
// 1 0
e.isEnable = (e.status === '已过期' || !e.orderEnable) ? '禁用' : '启用'
})
this.list = data
this.listAll = data
uni.hideLoading()
}).catch(e => {
uni.hideLoading()
})
},
//
subFinsh(val) {
const { orderStatus, productStatus } = val
this.orderStatus = orderStatus.length ? orderStatus[0] : ''
this.productStatus = productStatus.length ? productStatus[0] : ''
this.filter()
},
//
filter() {
const list = this.listAll
const { orderStatus, productStatus, keyword, curTab } = this
this.list = list.filter(e => (orderStatus === '' || ((orderStatus === 2 && e.status === '已过期') || (orderStatus === 1 && e.status === '生效中'))) && (productStatus === '' || ((productStatus === 2 && e.isEnable === '禁用') || (productStatus === 1 && e.isEnable === '启用'))) && e.productName.includes(keyword) && (curTab === '' || (curTab === e.productType)))
},
// tab
tabChange(tab) {
this.curTab = tab.id
this.filter()
},
//
toDetail(item) {
this.$util.to(`../clientDetail/clientDetail?customerId=${item.customerId}&show=1`)
}
}
}
</script>
<style scoped lang="scss">
.filter {
margin-bottom: 10px;
}
.list {
li {
padding: 0 24rpx;
margin: 16rpx 24rpx;
background-color: #fff;
border-radius: 16rpx;
}
.pro-name {
display: flex;
align-items: center;
padding: 18rpx 0;
font-size: 30rpx;
color: #333;
border-bottom: 1px solid #E6E8ED;
.icon {
width: 52rpx;
margin-right: 20rpx;
}
}
.info {
padding: 12rpx 0;
}
.line {
display: flex;
padding: 12rpx 0;
}
.name {
margin-right: 10rpx;
font-size: 28rpx;
color: #999;
}
.val {
font-size: 28rpx;
color: #333;
}
}
</style>

@ -0,0 +1,311 @@
<template>
<view>
<view class="filter">
<uni-search-bar class="search" radius="30" placeholder="请输入学校名称,商务经理,订单号" v-model="keyword" clearButton="auto" cancelButton="none" />
<view :class="['sort', sort]" @click="switchSort"></view>
<uni-icons class="icon" custom-prefix="iconfont" type="icon-filter" size="22" color="#007eff" @click="popup = true"></uni-icons>
</view>
<ul class="tab">
<li v-for="(tab, i) in tabs" :class="{active: curTab === tab.id}" @click="tabChange(tab)">{{ tab.name }}</li>
</ul>
<template v-if="list.length">
<view class="list">
<uni-swipe-action>
<uni-swipe-action-item
v-for="item in list"
:threshold="0"
:right-options="delOption"
@click="del(item)"
>
<view class="item" @click="toDetail(item)">
<view class="c-name">{{ item.orderNumber }}</view>
<view class="info">
<view class="left">
<view v-if="curTab" class="line">
<text class="name">商务经理</text>
<text class="val">{{ item.businessManagerName }}</text>
</view>
<view class="line">
<text class="name">客户名称</text>
<text class="val">{{ item.customerName }}</text>
</view>
<view class="line">
<text class="name">订单金额</text>
<text class="val">{{ item.orderAmount }}</text>
</view>
<view class="line">
<text class="name">订单内容</text>
<view class="val ell-wrap">
<view :class="{ell: !item.toggle}">{{ item.orderContent }}</view>
<view v-if="item.orderContent.length > 14" class="toggle" @click.stop="toggle(item)">{{ item.toggle ? '收起' : '展开' }}</view>
</view>
</view>
<view class="line">
<text class="name">下单日期</text>
<text class="val">{{ item.createTime }}</text>
</view>
</view>
<view :class="['type', 'type' + item.orderStatus]">
{{ filterData[0].data.find(e => e.value === item.orderStatus).title }}
</view>
</view>
</view>
</uni-swipe-action-item>
</uni-swipe-action>
</view>
<uni-load-more :status="status" />
</template>
<empty v-else></empty>
<uni-icons class="plus" type="plus-filled" size="60" color="#007eff" @click="$util.to('../orderDetail/orderDetail')"></uni-icons>
<filter-popup :data="filterData" :form.sync="filterForm" v-model="popup" title="全部筛选" height="1104rpx" @finsh="subFinsh"></filter-popup>
</view>
</template>
<script>
import { queryCustomer } from '@/apis/modules/client.js'
import { list, del } from '@/apis/modules/order.js'
export default {
data() {
return {
popup: false,
//
filterData: [
{
children: false,//
title: "订单状态",
key: "orderStatus", //
keyValue: "value", //
isRadio: true, //
data: [
{
title: "待发货",
value: 0
},
{
title: "已完成",
value: 1
},
],
}
],
filterForm: {},
curTab: 0,
tabs: [
{
name: '我的订单',
id: 0
},
{
name: '团队全部订单',
id: 1
}
],
reachBottom: 0, // 0->,1->,-1->
status: 'more', // more|loading|noMore
searchTimer: null,
orderStatus: '',
sort: 'desc',
keyword: '',
list: [],
page: 1,
pageSize: 10,
delOption: [{
text: '删除',
style: {
backgroundColor: '#F56C6C'
}
}],
}
},
watch: {
keyword () {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.initList()
}, 500)
}
},
//
onPullDownRefresh() {
this.initList()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1500)
},
//
onReachBottom() {
if (this.reachBottom >= 0) {
this.reachBottom = 1
this.status = 'loading'
this.getList()
}
},
onShow() {
//
try {
uni.removeStorageSync('courses')
} catch (e) {}
this.initRole()
},
methods: {
//
initRole() {
this.tabs = []
const auth = uni.getStorageSync('auth')
auth.includes('首页:订单:我的订单') && this.tabs.push({
name: '我的订单',
id: 0
})
auth.includes('首页:订单:团队全部订单') && this.tabs.push({
name: '团队全部订单',
id: 1
})
const len = this.tabs.length
// tab
if (len === 1) {
this.curTab = this.tabs[0].id
this.tabs = []
}
this.initList()
},
getList() {
const data = {
businessManagerId: this.$util.getBmId(),
teamId: uni.getStorageSync('team').id,
keywords: this.keyword,
pageNum: this.page,
pageSize: this.pageSize,
sort: this.sort,
orderStatus: this.orderStatus,
type: this.curTab
}
uni.showLoading({
title: '加载中'
})
list(data).then(({ data }) => {
uni.hideLoading()
// list
const list = data.records
list.map(e => {
e.toggle = e.orderContent.length < 14 // 14
})
this.list = this.reachBottom > 0 ? [...this.list, ...list] : list
this.page++ // page+1
const noMore = this.list.length === data.total //
this.status = noMore ? 'noMore' : 'more' // noMore
this.reachBottom = noMore ? -1 : 0 // -1
}).catch(e => {
uni.hideLoading()
})
},
initList() {
this.page = 1
this.reachBottom = 0
this.getList()
},
//
toggle(item) {
item.toggle = !item.toggle
},
//
subFinsh(val) {
const { orderStatus } = val
this.orderStatus = orderStatus.length ? orderStatus[0] : ''
this.initList()
},
//
switchSort() {
this.sort = this.sort === 'desc' ? 'asc' : 'desc'
this.initList()
},
// tab
tabChange(tab) {
this.curTab = tab.id
this.initList()
},
//
toDetail(item) {
this.$util.to(`../orderDetail/orderDetail?orderId=${item.orderId}&show=1`)
},
//
del(e) {
const that = this
uni.showModal({
title: '提示',
content: '确定要删除吗?',
success(res) {
if (res.confirm) {
del({
ids: [e.orderId]
}).then(res => {
that.$util.sucMsg('删除成功')
that.initList()
}).catch(res => {})
}
}
})
},
}
}
</script>
<style scoped lang="scss">
.list {
margin-top: 20rpx;
background-color: #fff;
.item {
padding: 20rpx 40rpx;
border-bottom: 1px solid #f1f1f1;
}
.c-name {
font-size: 30rpx;
color: #333;
}
.info {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10rpx;
}
.left {
max-width: 70%;
}
.line {
display: flex;
padding: 10rpx 0;
}
.name {
margin-right: 10rpx;
white-space: nowrap;
font-size: 28rpx;
color: #999;
}
.val {
max-width: 88%;
font-size: 28rpx;
color: #333;
}
.ell {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.toggle {
margin-top: 10rpx;
white-space: nowrap;
font-size: 24rpx;
color: #0e92ef;
}
.type {
margin-left: 20rpx;
font-size: 28rpx;
color: #ff7b2d;
white-space: nowrap;
}
.type1 {
color: #bdbdbd;
}
}
</style>

@ -0,0 +1,12 @@
{
"id": "filter-popup",
"name": "筛选 菜单 筛选菜单 上拉筛选 ",
"version": "1.0.4",
"description": "筛选菜单,支持单选和多选 , 选择后的数据通过.sync双向绑定,全端支持 导入即用",
"keywords": [
"筛选",
"菜单",
"筛选菜单",
"上拉筛选"
]
}

@ -0,0 +1,231 @@
{
"pages": [
{
"path" : "pages/login/login",
"style" :
{
"navigationBarTitleText": "登录",
"enablePullDownRefresh": false
}
},
{
"path" : "pages/index/index",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
},
{
"path" : "pages/qrcode/qrcode",
"style" :
{
"navigationBarTitleText": "邀请加入",
"enablePullDownRefresh": false
}
},
{
"path" : "pages/clients/clients",
"style" :
{
"navigationBarTitleText": "客户列表",
"enablePullDownRefresh": true
}
},
{
"path" : "pages/products/products",
"style" :
{
"navigationBarTitleText": "产品",
"enablePullDownRefresh": true
}
},
{
"path" : "pages/clientDetail/clientDetail",
"style" :
{
"navigationBarTitleText": "客户详情",
"enablePullDownRefresh": false
}
},
{
"path" : "pages/addStaff/addStaff",
"style" :
{
"navigationBarTitleText": "邀请成员",
"enablePullDownRefresh": false
}
},
{
"path" : "pages/plans/plans",
"style" :
{
"navigationBarTitleText": "产品方案",
"enablePullDownRefresh": false
}
},
{
"path" : "pages/person/person",
"style" :
{
"navigationBarTitleText": "我的",
"enablePullDownRefresh": false
}
},
{
"path" : "pages/send/send",
"style" :
{
"navigationBarTitleText": "下载发送",
"enablePullDownRefresh": false
}
},
{
"path" : "pages/password/password",
"style" :
{
"navigationBarTitleText": "修改密码",
"enablePullDownRefresh": false
}
},
{
"path" : "pages/setting/setting",
"style" :
{
"navigationBarTitleText": "设置",
"enablePullDownRefresh": false
}
},
{
"path" : "pages/teams/teams",
"style" :
{
"navigationBarTitleText": "团队",
"enablePullDownRefresh": true
}
},
{
"path" : "pages/account/account",
"style" :
{
"navigationBarTitleText": "修改账号",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/phone/phone",
"style" :
{
"navigationBarTitleText": "修改手机号",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/email/email",
"style" :
{
"navigationBarTitleText": "修改邮箱",
"enablePullDownRefresh": false
}
}
],
"subPackages": [{
"root": "order",
"pages": [
{
"path": "orders/orders",
"style" :
{
"navigationBarTitleText": "订单列表",
"enablePullDownRefresh": true
}
},
{
"path" : "addCourse/addCourse",
"style" :
{
"navigationBarTitleText": "选择产品",
"enablePullDownRefresh": true
}
},
{
"path" : "orderDetail/orderDetail",
"style" :
{
"navigationBarTitleText": "订单详情",
"enablePullDownRefresh": false
}
},
{
"path" : "ordered/ordered",
"style" :
{
"navigationBarTitleText": "已订阅产品",
"enablePullDownRefresh": false
}
},
{
"path" : "editCourse/editCourse",
"style" :
{
"navigationBarTitleText": "编辑产品",
"enablePullDownRefresh": false
}
},
{
"path" : "curClient/curClient",
"style" :
{
"navigationBarTitleText": "订单记录",
"enablePullDownRefresh": false
}
}
]
}],
"preloadRule": {
"order/orders/orders": {
"network": "all",
"packages": ["__APP__"]
}
},
"condition": { //
"current": 0, //list
"list": [{
"name": "test", //
"path": "pages/login/login" //
}]
},
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "或然中台",
"navigationBarBackgroundColor": "#007EFF",
"backgroundColor": "#f5f5f5",
"app-plus": {
"background": "#efeff4"
}
},
"tabBar": {
"color": "#B8B9B8",
"selectedColor": "#007FFF",
"borderStyle": "white",
"backgroundColor": "#ffffff",
"fontSize": "16px",
"iconWidth": "30px",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/image/tab1.png",
"selectedIconPath": "static/image/tab1-1.png",
"text": "首页"
}, {
"pagePath": "pages/teams/teams",
"iconPath": "static/image/tab2.png",
"selectedIconPath": "static/image/tab2-1.png",
"text": "团队"
}, {
"pagePath": "pages/person/person",
"iconPath": "static/image/tab3.png",
"selectedIconPath": "static/image/tab3-1.png",
"text": "我的"
}]
}
}

@ -0,0 +1,61 @@
<template>
<view class="page">
<view class="input">
<uni-easyinput v-model.trim="account" placeholder="请输入账号" @input="accountChange"></uni-easyinput>
</view>
<button type="primary" @click="submit">确认</button>
</view>
</template>
<script>
import { checkIfAnAccountExists, changeAccount } from '@/apis/modules/user.js'
export default {
data() {
return {
timer: null,
account: '',
repeat: false
}
},
onShow() {
},
methods: {
//
accountChange() {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
checkIfAnAccountExists(this.account).then(() => {
this.repeat = false
}).catch(e => {
this.repeat = true
})
}, 500)
},
submit() {
const { account } = this
if(!account) return this.$util.errMsg('请输入账号')
if (this.repeat) return this.$util.errMsg('账号已存在!')
changeAccount(account).then(res => {
this.$util.sucMsg('修改成功!')
setTimeout(() => {
uni.navigateBack()
}, 1000)
}).catch(e => {})
}
}
}
</script>
<style scoped lang="scss">
.page {
padding: 20px;
background-color: #fff;
}
/deep/.input {
margin-bottom: 15px;
.is-input-border {
border-color: #dedede !important;
}
}
</style>

@ -0,0 +1,51 @@
<template>
<view>
<uni-section title="邀请成员" type="line">
<uni-list>
<uni-list-item thumb-size="sm" showArrow title="二维码邀请" clickable :to="`../qrcode/qrcode`">
<template v-slot:header>
<view class="slot-box">
<image class="icon" src="@/static/image/qrcode.png" mode="widthFix"></image>
</view>
</template>
<template v-slot:body>
<text class="slot-box text">二维码邀请</text>
</template>
</uni-list-item>
</uni-list>
</uni-section>
</view>
</template>
<script>
export default {
data() {
return {
extraIcon: {
color: '#007eff',
size: '22',
type: 'icon-qrcode'
}
}
},
methods: {
}
}
</script>
<style scoped lang="scss">
.slot-box {
display: flex;
flex-direction: row;
align-items: center;
}
.icon {
width: 25px;
height: 25px;
margin-right: 10px;
}
.text {
font-size: 14px;
}
</style>

@ -0,0 +1,372 @@
<template>
<view class="page">
<uni-card v-if="customerId" :is-shadow="false" :border="false" is-full>
<view class="info">
<view class="name"><image class="school" src="@/static/image/school.png" mode="widthFix"></image>{{ form.customerName }}</view>
<view class="text">客户类型{{ customerType }}</view>
<view class="text">产品到期时间{{ form.expireDate || '' }}</view>
</view>
</uni-card>
<view class="block">
<view class="l-title">基本信息</view>
<view class="form-list">
<view :class="['line req', {err: err === 'customerName'}]">
<view class="name">客户名称</view>
<view v-if="isDetail" class="val">{{ form.customerName }}</view>
<view v-else :class="['ph', {val: form.customerName}]" @click="schoolVisible = true">{{ form.customerName || '请选择学校' }}</view>
</view>
<view class="line">
<view class="name">省份</view>
<view class="val">{{ form.provinceName }}</view>
</view>
<view class="line">
<view class="name">城市</view>
<view class="val">{{ form.cityName }}</view>
</view>
<view :class="['line req', {err: err === 'industryClass'}]">
<view class="name">行业类型</view>
<view v-if="isDetail" class="val">{{ form.industryClassName }}</view>
<uni-data-picker v-else class="picker-input" placeholder="请选择行业类型" popup-title="请选择行业类型" preload :clear-icon="false" :localdata="industryClassList" :map="{text: 'industryClassName', value: 'industryClassId'}" v-model="form.industryClassId" @change="industryClassChange"></uni-data-picker>
</view>
<view :class="['line req', {err: err === 'industry'}]">
<view class="name">行业</view>
<view v-if="isDetail" class="val">{{ form.industryName }}</view>
<uni-data-picker v-else class="picker-input" placeholder="请选择行业" popup-title="请选择行业" preload :clear-icon="false" :localdata="industryList" :map="{text: 'industryName', value: 'industryId'}" v-model="form.industryId" :readonly="form.industryClassId ? false : true" @change="handleErr('industry')"></uni-data-picker>
</view>
<view :class="['line req', {err: err === 'name'}]">
<view class="name">联系人姓名</view>
<view v-if="isDetail" class="val">{{ form.name }}</view>
<input v-else type="text" placeholder="请输入" v-model="form.name" @change="handleErr('name')">
</view>
<view class="line">
<view class="name">手机</view>
<view v-if="isDetail" class="val">{{ form.phone }}</view>
<input v-else type="number" maxlength="11" placeholder="请输入" v-model="form.phone">
</view>
<view :class="['line req', {err: err === 'account'}]">
<view class="name">账号</view>
<view v-if="isDetail" class="val">{{ form.account }}</view>
<input v-else type="text" placeholder="请以院校首字母+admin的格式来设置" v-model="form.account" @change="handleErr('account')">
</view>
<view class="line">
<view class="name">职务</view>
<view v-if="isDetail" class="val">{{ form.position }}</view>
<input v-else type="text" placeholder="请输入" v-model="form.position">
</view>
<view class="line">
<view class="name">邮箱</view>
<view v-if="isDetail" class="val">{{ form.email }}</view>
<input v-else type="text" placeholder="请输入" v-model="form.email">
</view>
<view v-if="customerId" class="line">
<view class="name">产品到期时间</view>
<view class="val">{{ form.expireDate }}</view>
</view>
<view v-if="customerId" class="line">
<view class="name">客户类型</view>
<view class="val">{{ customerType }}</view>
</view>
</view>
</view>
<view v-if="isDetail" class="action">
<view class="item" @click="toPage(`../clientDetail/clientDetail?customerId=${customerId}`)">
<uni-icons class="icon" custom-prefix="iconfont" type="icon-edit" size="30" color="#959595"></uni-icons>
<view class="text">编辑</view>
</view>
<view class="item" @click="toPage(`/order/ordered/ordered?customerId=${customerId}`)">
<uni-icons class="icon" custom-prefix="iconfont" type="icon-product" size="30" color="#959595"></uni-icons>
<view class="text">已订阅产品</view>
</view>
<view class="item" @click="toPage(`/order/curClient/curClient?customerId=${customerId}&name=${form.customerName}`)">
<uni-icons class="icon" custom-prefix="iconfont" type="icon-dingdan" size="30" color="#959595"></uni-icons>
<view class="text">订单</view>
</view>
</view>
<view v-else class="btn-wrap">
<view class="btn" @click="submit">确定</view>
</view>
<view class="popup-mask" v-show="schoolVisible" @click="closeSchool"></view>
<view class="popup" v-show="schoolVisible">
<view class="top">请选择学校</view>
<uni-icons class="close" type="closeempty" size="20" @click="closeSchool"></uni-icons>
<uni-search-bar class="search" radius="5" placeholder="请输入学校名称" v-model="keyword" clearButton="auto" cancelButton="none" />
<view class="list">
<view class="item" v-for="item in schoolList" @click="schoolChange(item)">{{ item.schoolName }}</view>
</view>
</view>
</view>
</template>
<script>
import { queryCustomerDetails, querySchool, queryIndustryClass, queryIndustry, queryCustomerIsExists, addCustomer, updateCustomer } from '@/apis/modules/client.js'
export default {
data() {
return {
isDetail: false,
customerId: '',
//
customerTypeList: [{
name: '正式',
value: 1
},
{
name: '试用',
value: 2
},
{
name: '到期',
value: 3
}],
nameRepeat: false,
schoolVisible: false,
keyword: '',
searchTimer: null,
schoolList: [],
schoolListAll: [],
industryClassList: [],
industryList: [],
form: {
countries: '中国',
customerId: '',
customerName: '',
industryClassId: '',
industryId: '',
provinceId: '',
provinceName: '',
account: '',
name: '',
phone: '',
position: '',
cityId: '',
cityName: '',
customerType: '',
expireDate: '',
email: '',
schoolId: ''
},
customerType: '',
err: ''
}
},
watch: {
keyword () {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.filterSchool()
}, 500)
}
},
onShow() {
const pages = getCurrentPages()
const { options } = pages[pages.length - 1]
this.customerId = options.customerId
this.isDetail = !!options.show
options.customerId && this.getInfo()
//
if (!this.isDetail) {
this.getSchool()
this.getIndustryClass()
}
//
uni.setNavigationBarTitle({
title: options.customerId ?
(options.show ?
'客户详情' :
'编辑客户') :
'新增客户'
})
},
methods: {
//
getInfo() {
uni.showLoading({
title: '加载中'
})
queryCustomerDetails({
customerId: this.customerId
}).then(({ result }) => {
const { customer } = result
this.form = customer
!this.isDetail && customer.industryClassId && this.getIndustry()
this.customerType = this.customerTypeList.find(e => e.value === customer.customerType).name
uni.hideLoading()
}).catch(e => {
uni.hideLoading()
})
},
//
getSchool() {
querySchool({
schoolName: '',
provinceId: '',
cityId: ''
}).then(({ list }) => {
this.schoolListAll = list
this.schoolList = list
}).catch(res => {})
},
//
filterSchool() {
const { keyword } = this
this.schoolList = keyword ?
this.schoolListAll.filter(e => e.schoolName.includes(keyword)) :
this.schoolListAll
},
//
closeSchool() {
this.schoolVisible = false
this.keyword = ''
},
//
schoolChange(school) {
const { form } = this
const { schoolId } = school
form.schoolId = schoolId
form.customerName = school.schoolName
form.provinceId = school.provinceId
form.provinceName = school.provinceName
form.cityId = school.cityId
form.cityName = school.cityName
this.closeSchool()
//
queryCustomerIsExists({
schoolId
}).then(res => {
this.nameRepeat = false
this.handleErr('customerName')
}).catch(res => {
this.nameRepeat = true
this.err = 'customerName'
})
},
//
handleErr(val) {
if (val === this.err) this.err = ''
},
//
getIndustryClass() {
queryIndustryClass().then(({ list }) => {
this.industryClassList = list
}).catch(res => {})
},
//
industryClassChange() {
this.handleErr('industryClass')
this.getIndustry()
},
//
getIndustry() {
queryIndustry({
industryClassId: this.form.industryClassId
}).then(({ list }) => {
this.industryList = list
}).catch(res => {})
},
//
toPage(path) {
this.$util.to(path)
},
//
submit() {
const refs = this.$refs
const { form } = this
const { schoolId, industryClassId, industryId, name, account } = form
if (!schoolId) {
this.err = 'customerName'
return this.$util.errMsg('请选择客户!')
} if (this.nameRepeat) {
this.err = 'customerName'
return this.$util.errMsg('客户已存在!')
} if (!industryClassId) {
this.err = 'industryClass'
return this.$util.errMsg('请选择行业类型!')
} if (!industryId) {
this.err = 'industry'
return this.$util.errMsg('请选择行业!')
} if (!name) {
this.err = 'name'
return this.$util.errMsg('请输入联系人姓名!')
} if (!account) {
this.err = 'account'
return this.$util.errMsg('请输入账号!')
}
uni.showLoading({
title: '提交中'
})
if (this.customerId) {
updateCustomer(form).then(res => {
uni.hideLoading()
this.$util.sucMsg('编辑成功')
setTimeout(() => {
uni.navigateBack()
}, 1500)
}).catch(res => {})
} else {
addCustomer(form).then(({ customerId }) => {
uni.hideLoading()
const that = this
uni.showModal({
title: '提示',
content: '创建客户成功,是否马上为该客户创建订单?',
success: function (res) {
if (res.confirm) {
//
uni.redirectTo({
url: `/order/orderDetail/orderDetail?customerId=${customerId}`
})
} else if (res.cancel) {
uni.navigateBack()
}
}
})
}).catch(res => {})
}
},
}
}
</script>
<style scoped lang="scss">
.page {
padding-bottom: 170rpx;
-webkit-overflow-scrolling: touch;
}
.info {
.name {
display: flex;
align-items: center;
margin-bottom: 5px;
font-size: 16px;
color: #333;
}
.school {
width: 48rpx;
margin-right: 10rpx;
}
}
.action {
z-index: 2;
position: fixed;
bottom: 0;
display: flex;
justify-content: space-around;
width: 100%;
padding: 5px 10px;
padding-bottom: env(safe-area-inset-bottom);
background-color: #fff;
border-top: 1px solid #f3f3f3;
box-shadow: 0 -2px 2px #f5f5f5;
box-sizing: border-box;
.item {
text-align: center;
}
.text {
font-size: 10px;
color: #959595;
}
}
</style>

@ -0,0 +1,244 @@
<template>
<view class="page">
<view class="filter">
<uni-search-bar class="search" radius="30" placeholder="请输入客户名称" v-model="keyword" clearButton="auto" cancelButton="none" />
<uni-icons class="icon" custom-prefix="iconfont" type="icon-filter" size="22" color="#007eff" @click="popup = true"></uni-icons>
</view>
<ul class="tab">
<li v-for="(tab, i) in tabs" :class="{active: curTab === tab.id}" @click="tabChange(tab)">{{ tab.name }}</li>
</ul>
<template v-if="list.length">
<ul class="list">
<li v-for="item in list" @click="toDetail(item)">
<view class="c-name">{{ item.customerName }}</view>
<view class="info">
<view class="left">
<view class="line">
<text class="name">联系人</text>
<text class="val">{{ item.orderContact }}</text>
</view>
<view class="line">
<text class="name">账号</text>
<text class="val">{{ item.account }}</text>
</view>
<view class="line">
<text class="name">产品到期时间</text>
<text class="val">{{ item.expireDate.split(' ')[0] }}</text>
</view>
<view class="line">
<text class="name">商务经理</text>
<text class="val">{{ item.businessManagerName }}</text>
</view>
</view>
<view class="type">
{{ filterData[0].data.find(e => e.value === item.customerType).title }}客户
</view>
</view>
</li>
</ul>
<uni-load-more :status="status" />
</template>
<empty v-else text="您当前暂无有下单的客户,请快去给客户下订单吧"></empty>
<uni-icons class="plus" type="plus-filled" size="60" color="#007eff" @click="$util.to('../clientDetail/clientDetail')"></uni-icons>
<filter-popup :data="filterData" :form.sync="filterForm" v-model="popup" title="全部筛选" height="1104rpx" @finsh="subFinsh"></filter-popup>
</view>
</template>
<script>
import { list, all } from '@/apis/modules/client.js'
export default {
data() {
return {
popup: false,
//
filterData: [
{
children: false,//
title: "客户类型",
key: "customerType", //
keyValue: "value", //
isRadio: true, //
data: [
{
title: '正式',
value: 1
},
{
title: '试用',
value: 2
},
{
title: '到期',
value: 3
}
],
}
],
filterForm: {
customerType: []
},
curTab: 0,
tabs: [],
reachBottom: 0, // 0->,1->,-1->
status: 'more', // more|loading|noMore
searchTimer: null,
customerType: '',
keyword: '',
list: [],
page: 1,
pageSize: 10
}
},
watch: {
keyword () {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.initList()
}, 500)
}
},
//
onPullDownRefresh() {
this.initList()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1500)
},
//
onReachBottom() {
if (this.reachBottom >= 0) {
this.reachBottom = 1
this.status = 'loading'
this.getList()
}
},
onShow() {
this.initRole()
},
methods: {
//
initRole() {
this.tabs = []
const auth = uni.getStorageSync('auth')
auth.includes('首页:客户:我的客户') && this.tabs.push({
name: '我的客户',
id: 0
})
auth.includes('首页:客户:团队全部客户') && this.tabs.push({
name: '团队全部客户',
id: 1
})
const len = this.tabs.length
// tab
if (len === 1) {
this.curTab = this.tabs[0].id
this.tabs = []
}
this.initList()
},
//
getList() {
const data = {
businessManagerId: this.$util.getBmId(),
teamId: uni.getStorageSync('team').id,
customerType: this.customerType,
keywords: this.keyword,
pageNum: this.page,
pageSize: this.pageSize,
type: this.curTab // 团队:1 / 个人:0
}
uni.showLoading({
title: '加载中'
})
all(data).then(({ data }) => {
uni.hideLoading()
// list
this.list = this.reachBottom > 0 ? [...this.list, ...data.records] : data.records
this.page++ // page+1
const noMore = this.list.length === data.total //
this.status = noMore ? 'noMore' : 'more' // noMore
this.reachBottom = noMore ? -1 : 0 // -1
}).catch(e => {
uni.hideLoading()
})
},
initList() {
this.page = 1
this.reachBottom = 0
this.getList()
},
//
subFinsh(val) {
const { customerType } = val
this.customerType = customerType.length ? customerType[0] : ''
this.initList()
},
// tab
tabChange(tab) {
this.curTab = tab.id
this.initList()
},
//
toDetail(item) {
this.$util.to(`../clientDetail/clientDetail?customerId=${item.customerId}&show=1`)
}
}
}
</script>
<style scoped lang="scss">
.page {
padding-bottom: 90px;
}
.filter {
display: flex;
align-items: center;
.search {
flex: 1;
}
.sl-filter {
width: 30%;
margin-left: 10%;
}
}
.list {
margin-top: 20rpx;
background-color: #fff;
li {
padding: 20rpx 40rpx;
border-bottom: 1px solid #f1f1f1;
}
.c-name {
font-size: 30rpx;
color: #333;
}
.info {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10rpx;
}
.line {
display: flex;
padding: 10rpx 0;
}
.name {
width: 200rpx;
margin-right: 10rpx;
font-size: 28rpx;
color: #999;
}
.val {
max-width: 70%;
font-size: 28rpx;
color: #333;
}
.type {
font-size: 28rpx;
color: #ff7b2d;
white-space: nowrap;
}
}
</style>

@ -0,0 +1,113 @@
<template>
<view class="page">
<view class="input">
<uni-easyinput v-model="form.email" placeholder="请填写你的邮箱" :clearable="false" />
</view>
<view class="input code-wrap">
<uni-easyinput class="code" v-model="form.code" placeholder="验证码" :clearable="false" />
<view class="send-code" @click="sendCode" :disabled="codeDisabled">{{ btnText }}</view>
</view>
<button type="primary" @click="submit">确认</button>
</view>
</template>
<script>
import { updateMyEmail, mailCodeSend } from '@/apis/modules/user.js'
export default {
data() {
return {
userId: '',
form: {
email: '',
code: ''
},
codeDisabled: false,
phoneTimer: null,
btnText: '发送验证码',
}
},
onShow() {
},
methods: {
//
sendCode() {
const { email } = this.form
if (!email) return this.$util.errMsg('请输入邮箱')
if (!/^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/.test(email) && !/^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/.test(email)) return this.$util.errMsg('请输入正确的请输入邮箱')
mailCodeSend({
email,
platformId: 4
}).then(res => {
this.phoneCountdown()
}).catch(res => {})
},
//
phoneCountdown() {
let count = 60
if (!this.phoneTimer) {
this.codeDisabled = true
this.phoneTimer = setInterval(() => {
if (count > 0) {
count--
this.btnText = `${count}秒后重试`
} else {
this.codeDisabled = false
clearInterval(this.phoneTimer)
this.phoneTimer = null
this.btnText = `发送验证码`
}
}, 1000)
}
},
//
submit() {
const { email, code } = this.form
if (!email) return this.$util.errMsg('请输入邮箱')
if (!code) return this.$util.errMsg('请输入验证码')
updateMyEmail({
email,
code,
platformId: 4
}).then(res => {
this.$util.sucMsg('修改成功!')
setTimeout(() => {
uni.navigateBack()
}, 1500)
}).catch(res => {})
}
}
}
</script>
<style scoped lang="scss">
.page {
padding: 20px;
background-color: #fff;
}
/deep/.input {
margin-bottom: 15px;
.is-input-border {
border-color: #dedede !important;
}
}
.input {
margin-bottom: 20px;
}
.code-wrap {
display: flex;
}
.code {
flex: 1;
}
.send-code {
width: 100px;
margin-left: 20px;
text-align: center;
color: #4386ff;
font-size: 12px;
line-height: 36px;
border: 1px solid #4386ff;
border-radius: 5px;
}
</style>

@ -0,0 +1,349 @@
<template>
<view :class="['page', {oh: !per}]">
<image class="bg" src="@/static/image/index/index2.png" mode="widthFix"></image>
<view class="team">
<uni-data-picker class="picker-input" placeholder="切换团队" popup-title="切换团队" preload :clear-icon="false" :localdata="list" :map="{text: 'partnerClassificationName', value: 'teamId'}" v-model="teamId" @change="teamChange"></uni-data-picker>
</view>
<view class="banner">
<image class="img" src="@/static/image/index/index1.png" mode="widthFix"></image>
<view class="info">
<view class="title">城市合伙人招募中</view>
<view class="text">携手共创教育信息化新未来合伙共享产业互联领域新红利</view>
</view>
</view>
<ul class="entry">
<li v-if="auth('首页:客户')" @click="$util.to('../clients/clients')">
<image class="icon" src="@/static/image/index/index3.png" mode="widthFix"></image>
<view class="text">客户</view>
</li>
<li @click="$util.to('../plans/plans')">
<image class="icon" src="@/static/image/index/index4.png" mode="widthFix"></image>
<view class="text">方案</view>
</li>
<li v-if="auth('首页:订单')" @click="$util.to('/order/orders/orders')">
<image class="icon" src="@/static/image/index/index5.png" mode="widthFix"></image>
<view class="text">订单</view>
</li>
<li @click="$util.to('../products/products')">
<image class="icon" src="@/static/image/index/index6.png" mode="widthFix"></image>
<view class="text">产品</view>
</li>
</ul>
<view class="panel">
<view class="title" @click="toPanel(1)">
<view class="left">
<image class="icon" src="@/static/image/index/index8.png" mode="widthFix"></image>
销售进展
</view>
<view class="right">
<image class="date" src="@/static/image/index/index7.png" mode="widthFix"></image>
<text>本月</text>
<image class="arrow" src="@/static/image/index/index10.png" mode="widthFix"></image>
</view>
</view>
<view class="data first">
<view class="line">
<view class="item">
<view class="name">本月新增客户</view>
<view class="val">0</view>
</view>
<view class="item">
<view class="name">我的客户</view>
<view class="val">0</view>
</view>
</view>
<view class="line">
<view class="item">
<view class="name">本月新增产品试用</view>
<view class="val">0</view>
</view>
<view class="item">
<view class="name">试用客户</view>
<view class="val">0</view>
</view>
</view>
</view>
</view>
<view class="panel">
<view class="title y-title" @click="toPanel(1)">
<view class="left">
<image class="icon" src="@/static/image/index/index9.png" mode="widthFix"></image>
年度业绩看板
</view>
<image class="arrow" src="@/static/image/index/index10.png" mode="widthFix"></image>
</view>
<view class="data second">
<view class="line">
<view class="item">
<view class="val">0</view>
<view class="name">成交总额</view>
</view>
<view class="item">
<view class="val">0</view>
<view class="name">收益金额</view>
</view>
<view class="item">
<view class="val">0</view>
<view class="name">已到账</view>
</view>
<view class="item">
<view class="val">0</view>
<view class="name">未到账</view>
</view>
</view>
</view>
</view>
<view v-if="!per" class="per-mask">功能升级中敬请期待...</view>
</view>
</template>
<script>
import { getUserRolesPermissionMenu } from '@/apis/modules/user.js'
import { getTeamsByAccountId, getTheBusinessManagerIdsUnderTheTeam } from '@/apis/modules/client.js'
import { treeList } from '@/apis/modules/parner.js'
export default {
data() {
return {
per: true, //
// teamId: uni.getStorageSync('team').teamId || '',
teamId: '',
list: [],
id: '',
teamList: []
}
},
onShow() {
this.per = true
this.getInfo()
},
methods: {
//
initRole() {
if (!uni.getStorageSync('auth').includes('首页')) {
this.per = false
}
},
//
getAuth() {
uni.getStorageSync('token') && getUserRolesPermissionMenu({
teamId: this.list.find(e => e.teamId === this.teamId).partnerClassificationId,
platformId: 4
}).then(({ permissionMenu }) => {
uni.hideLoading()
const auth = []
//
const generateAuth = (list, parent) => {
list.map(e => {
const name = `${parent ? parent + ':' : ''}${e.name}`
auth.push(name)
generateAuth(e.children, name)
})
}
generateAuth(permissionMenu[0].children, '')
uni.setStorageSync('auth', auth)
this.$forceUpdate()
this.initRole()
}).catch(e => {
uni.hideLoading()
uni.setStorageSync('auth', [])
this.initRole()
})
},
//
getInfo() {
uni.showLoading({
title: '加载中'
})
getTeamsByAccountId().then(({ data }) => {
data.map(e => {
const n = e.partnerClassificationList
e.id = n.id
e.teamId = e.isTeam == 1 ? e.partnerClassificationId : n.id
e.partnerClassificationName = n.partnerClassificationName
delete e.partnerClassificationList
})
if (data.length) {
/**
* @description 如果是第一次进则默认选中第一个团队并把该团队的信息存入缓存
* 或者团队列表里没有该id则说明超管已经被转让也需要重新选中团队
*/
if (!this.teamId || !data.find(e => e.teamId == this.teamId)) {
this.teamId = data[0].teamId
uni.setStorageSync('team', data[0])
}
} else {
// 退
uni.hideLoading()
uni.clearStorageSync()
uni.navigateTo({
url: '../login/login'
})
}
this.list = data
this.getAuth()
}).catch(e => {
uni.hideLoading()
})
},
//
teamChange() {
const { teamId } = this
const e = this.list.find(e => e.teamId == teamId)
uni.setStorageSync('team', e)
this.getAuth()
},
//
toPanel(i) {
this.$util.errMsg('功能暂未开放!')
}
}
}
</script>
<style scoped lang="scss">
.page {
position: relative;
min-height: 100%;
padding: 30rpx 22rpx;
box-sizing: border-box;
}
.bg {
z-index: -1;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.team {
width: 300rpx;
margin-bottom: 30rpx;
}
.banner {
position: relative;
.img {
width: 100%;
}
.info {
position: absolute;
top: 80rpx;
left: 46rpx;
}
.title {
margin-bottom: 15rpx;
font-size: 36rpx;
font-weight: 600;
color: #001D67;
}
.text {
font-size: 20rpx;
font-family: PingFangSC-Regular, PingFang SC;
color: #001D67;
}
}
.entry {
display: flex;
justify-content: space-around;
align-items: center;
margin: 10rpx 0 30rpx;
text-align: center;
.icon {
width: 78rpx;
}
.text {
font-size: 28rpx;
color: #333;
}
}
.panel {
margin: 20rpx 10rpx;
background-color: #fff;
border-radius: 20rpx;
overflow: hidden;
.title {
display: flex;
justify-content: space-between;
align-items: center;
padding: 17rpx 20rpx;
background: linear-gradient(90deg, #E5EFFF 0%, #FFFFFF 100%);
}
.y-title {
background: linear-gradient(90deg, #FFF5E5 0%, #FFFFFF 100%);
}
.left {
display: flex;
align-items: center;
font-size: 30rpx;
color: #333;
}
.right {
display: flex;
align-items: center;
font-size: 28rpx;
color: #333;
text {
margin: 0 15rpx 0 8rpx;
}
}
.icon {
width: 36rpx;
margin-right: 10rpx;
}
.date {
width: 26rpx;
}
.arrow {
width: 16rpx;
}
.data {
padding: 33rpx 36rpx;
}
.line {
display: flex;
}
.first {
.item:first-child {
width: 65%;
}
.name {
margin-bottom: 14rpx;
font-size: 24rpx;
color: #999;
}
.val {
font-size: 30rpx;
color: #333;
}
.line:first-child {
margin-bottom: 32rpx;
}
}
.second {
padding: 38rpx 36rpx;
.line {
justify-content: space-between;
}
.item {
text-align: center;
}
.val {
margin-bottom: 10rpx;
font-size: 30rpx;
color: #333;
}
.name {
font-size: 24rpx;
color: #999;
}
}
}
.oh {
overflow: hidden;
}
</style>

@ -0,0 +1,236 @@
<template>
<view class="page">
<view class="wrap">
<image class="logo" src="@/static/image/logo.png" mode=""></image>
<view class="hello">Hi城市合伙人请登录</view>
<button v-if="isLogin && !getPhone" class="btn phone" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">
<image src="@/static/image/phone.png" mode="widthFix"></image>
绑定手机
</button>
<template v-else>
<view class="btn wechat" @click="login">
<image src="@/static/image/wechat.png" mode="widthFix"></image>
微信授权登录
</view>
<view class="btn phone">
<image src="@/static/image/phone.png" mode="widthFix"></image>
手机账号登录
</view>
</template>
<view class="agree">
<uni-data-checkbox class="check" multiple v-model="agree" :localdata="agreeData"></uni-data-checkbox>
<text @click="toAgreement(0)">用户服务协议</text>
<text @click="toAgreement(1)">用户隐私协议</text>
</view>
</view>
</view>
</template>
<script>
import { login, userBinding } from '@/apis/modules/user.js'
import WXBizDataCrypt from '@/libs/WXBizDataCrypt'
export default {
data() {
return {
agree: [],
agreeData: [{
text: '同意',
value: 1
}],
isLogin: false, //
getPhone: false ,//
sessionKey: '',
openId: '',
unionid: '',
submiting: false
}
},
onShow() {
//
this.checkLogin()
},
methods: {
//
checkLogin() {
if (uni.getStorageSync('token')) {
this.toIndex()
} else {
uni.clearStorageSync()
this.isLogin = false
this.getPhone = false
}
},
//
login() {
if (this.agree.length) {
if (this.submiting) return false
const that = this
uni.getUserProfile({
lang: 'zh_CN',
desc: '登录',
success: ({ userInfo }) => {
console.log(22, userInfo)
uni.setStorageSync('userName', userInfo.nickName)
uni.setStorageSync('avatar', userInfo.avatarUrl)
uni.login({
success: ({ code }) => {
if (code) {
this.submiting = true
login({
code,
avatarUrl: userInfo.avatarUrl
}).then(({ data }) => {
const e = data.sessionKey
this.sessionKey = e.session_key
this.openId = e.openid
this.unionid = e.unionid
uni.setStorageSync('sessionKey', e.session_key)
this.submiting = false
//
if (data.state === 'login') {
this.toIndex()
uni.setStorageSync('token', data.token)
} else {
this.isLogin = true
}
}).catch(e => {
this.submiting = false
})
} else {
this.submiting = false
that.$util.errMsg('登录失败!')
}
}
})
},
fail(res) {
that.$util.errMsg('登录授权失败!')
}
})
} else {
this.$util.errMsg('请先阅读勾选协议!')
}
},
//
onGetPhoneNumber(e){
if (this.submiting) return false
const { encryptedData, iv } = e.detail
// 1.使js2.使
// const WXBizDataCrypt = require('@/libs/WXBizDataCrypt')
const accountInfo = uni.getAccountInfoSync() // appid
const pc = new WXBizDataCrypt(accountInfo.miniProgram.appId , this.sessionKey)
const data = pc.decryptData(encryptedData , iv)
this.submiting = true
//
userBinding({
openId: this.openId,
phone: data.phoneNumber,
unionid: this.unionid
}).then(({ token }) => {
this.submiting = false
uni.setStorageSync('token', token)
this.toIndex()
}).catch(e => {
this.submiting = false
uni.showToast({
title: e.message,
icon: 'none'
})
})
},
toAgreement(id) {
this.$util.openFile(id)
},
//
toIndex() {
uni.reLaunch({
url: '../index/index'
})
}
}
}
</script>
<style scoped lang="scss">
.page {
min-height: calc(100vh - 170rpx);
padding-top: 170rpx;
background: url(@/static/image/login1.png) 0 0/175rpx auto no-repeat,
url(@/static/image/login2.png) bottom right/123rpx auto no-repeat;
overflow: hidden;
}
.wrap {
position: relative;
height: 60vh;
padding: 214rpx 74rpx 28rpx;
margin: 0 61rpx;
text-align: center;
background-color: #fff;
border-radius: 20rpx;
.logo {
width: 393rpx;
height: 93rpx;
}
.hello {
margin: 36rpx 0;
font-size: 28rpx;
color: #333;
}
.btn {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 88rpx;
font-size: 32rpx;
border-radius: 10rpx;
image {
width: 50rpx;
margin-right: 15rpx;
}
}
.wechat {
margin-bottom: 38rpx;
color: #fff;
background-color: #007EFF;
}
.phone {
color: #007EFF;
border: 1px solid #007EFF;
background-color: #fff;
image {
width: 40rpx;
}
}
.agree {
position: absolute;
bottom: 28rpx;
left: 0%;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
text-align: center;
font-size: 24rpx;
color: #ccc;
text {
color: #007EFF;
}
}
/deep/.check {
.checklist-box {
margin: 0;
}
.checkbox__inner {
width: 40rpx !important;
height: 40rpx !important;
border-radius: 50% !important;
}
.checkbox__inner-icon {
top: 8rpx !important;
left: 14rpx !important;
}
}
}
</style>

@ -0,0 +1,62 @@
<template>
<view class="page">
<view class="input">
<uni-easyinput type="password" v-model.trim="form.password" placeholder="请输入旧密码"></uni-easyinput>
</view>
<view class="input">
<uni-easyinput type="password" v-model.trim="form.newPassword" placeholder="请输入新密码"></uni-easyinput>
</view>
<view class="input">
<uni-easyinput type="password" v-model.trim="form.reNewPassword" placeholder="请再次输入新密码"></uni-easyinput>
</view>
<button type="primary" @click="submit">确认</button>
</view>
</template>
<script>
import { examinePassword } from '@/apis/modules/user.js'
export default {
data() {
return {
form: {
password: '',
newPassword: '',
reNewPassword: ''
},
}
},
methods: {
submit() {
const { form } = this
const { password, newPassword, reNewPassword } = form
if(!password) return this.$util.errMsg('请输入旧密码')
if(!newPassword) return this.$util.errMsg('请输入新密码')
if(!reNewPassword) return this.$util.errMsg('请确认新密码')
if(newPassword.length < 6 || reNewPassword.length < 6) return this.$util.errMsg('请输入6位数以上的密码')
if(newPassword !== reNewPassword) return this.$util.errMsg('输入的新密码不一致,请重新确认')
if(password === newPassword) return this.$util.errMsg('旧密码跟新密码不能一致')
form.accountId = uni.getStorageSync('team').accountId
examinePassword(form).then(res => {
this.$util.sucMsg('修改成功!')
setTimeout(() => {
uni.navigateBack()
}, 1000)
}).catch(e => {})
}
}
}
</script>
<style scoped lang="scss">
.page {
padding: 20px;
background-color: #fff;
}
/deep/.input {
margin-bottom: 15px;
.is-input-border {
border-color: #dedede !important;
}
}
</style>

@ -0,0 +1,140 @@
<template>
<view class="page">
<image class="bg" src="@/static/image/person-bg.png"></image>
<view class="wrap">
<view class="info">
<image class="avatar" :src="avatar" mode=""></image>
<view class="text">
<view class="name">{{ my.info.userName }}</view>
<view class="phone">{{ my.info.phone }}</view>
</view>
</view>
<view class="list">
<view v-if="auth('我的:我的收益')" class="item">
<view class="left">
<image class="icon" src="@/static/image/person1.png" mode=""></image>
<text class="name">我的项目收益</text>
</view>
<text class="val">{{ my.myIncome}}</text>
</view>
<view v-if="auth('我的:团队收益')" class="item">
<view class="left">
<image class="icon" src="@/static/image/person2.png" mode=""></image>
<text class="name">团队收益</text>
</view>
<text class="val">{{ my.teamIncome}}</text>
</view>
<view v-if="auth('我的:设置')" class="item" @click="$util.to('../setting/setting')">
<view class="left">
<image class="icon" src="@/static/image/person3.png" mode=""></image>
<text class="name">设置</text>
</view>
</view>
</view>
</view>
<view v-if="!per" class="per-mask">功能升级中敬请期待...</view>
</view>
</template>
<script>
import { my } from '@/apis/modules/parner.js'
export default {
data() {
return {
per: true, //
my: {
info: {
phone: ''
},
myIncome: 0,
teamIncome: 0
},
avatar: uni.getStorageSync('avatar') || '@/static/image/avatar.png',
userName: uni.getStorageSync('userName')
}
},
onShow() {
this.per = true
this.initRole()
},
methods: {
//
initRole() {
if (!uni.getStorageSync('auth').includes('我的')) {
this.per = false
} else {
this.getInfo()
}
},
//
getInfo() {
const team = uni.getStorageSync('team')
my({
partnerId: team.partnerId,
teamId: team.teamId
}).then(({ my }) => {
this.my = my
}).catch(e => {})
}
}
}
</script>
<style scoped lang="scss">
.bg {
width: 100%;
height: 300rpx;
}
.wrap {
position: relative;
padding: 0 24rpx;
margin-top: -150rpx;
}
.info {
display: flex;
align-items: center;
padding: 26rpx 34rpx;
background-color: #fff;
border-radius: 16rpx;
.avatar {
width: 120rpx;
height: 120rpx;
margin-right: 28rpx;
border-radius: 50%;
}
.name {
margin-bottom: 10rpx;
font-size: 40rpx;
color: #333;
}
.phone {
font-size: 28rpx;
color: #333;
}
}
.list {
margin-top: 16rpx;
.item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 26rpx 28rpx;
margin-bottom: 16rpx;
background-color: #fff;
border-radius: 16rpx;
}
.left {
display: inline-flex;
align-items: center;
}
.icon {
width: 48rpx;
height: 48rpx;
margin-right: 16rpx;
}
text {
font-size: 28rpx;
color: #333;
}
}
</style>

@ -0,0 +1,119 @@
<template>
<view class="page">
<view class="input">
<uni-easyinput type="number" v-model="form.phone" placeholder="请填写你的手机号" maxlength="11" :clearable="false" />
</view>
<view class="input code-wrap">
<uni-easyinput class="code" type="number" v-model="form.code" placeholder="验证码" maxlength="6" :clearable="false" />
<view class="send-code" @click="sendCode" :disabled="codeDisabled">{{ btnText }}</view>
</view>
<button type="primary" @click="submit">确认</button>
</view>
</template>
<script>
import { sendPhoneOrEmailCode, changePhoneNumber, checkIfThePhoneNumberExists } from '@/apis/modules/user.js'
export default {
data() {
return {
userId: '',
form: {
phone: '',
code: ''
},
codeDisabled: false,
phoneTimer: null,
phoneOpener: '',
btnText: '发送验证码',
}
},
onShow() {
},
methods: {
//
sendCode() {
const { phone } = this.form
if (!phone) return this.$util.errMsg('请输入手机号')
if (!/^1[3456789]\d{9}$/.test(phone) && !/^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/.test(phone)) return this.$util.errMsg('请输入正确的手机号')
//
checkIfThePhoneNumberExists(phone).then(() => {
sendPhoneOrEmailCode({
userId: this.userId,
phone,
types: 2
}).then(({ message }) => {
if (message.opener) {
this.phoneCountdown()
this.phoneOpener = message.opener
} else {
this.$util.errMsg(message)
}
}).catch(res => {})
}).catch(res => {})
},
//
phoneCountdown() {
let count = 60
if (!this.phoneTimer) {
this.codeDisabled = true
this.phoneTimer = setInterval(() => {
if (count > 0) {
count--
this.btnText = `${count}秒后重试`
} else {
this.codeDisabled = false
clearInterval(this.phoneTimer)
this.phoneTimer = null
this.btnText = `发送验证码`
}
}, 1000)
}
},
//
submit() {
const { phone, code } = this.form
if (!phone) return this.$util.errMsg('请输入手机号')
if (!code) return this.$util.errMsg('请输入验证码')
changePhoneNumber(phone, code).then(res => {
this.$util.sucMsg('修改成功!')
setTimeout(() => {
uni.navigateBack()
}, 1500)
}).catch(res => {})
}
}
}
</script>
<style scoped lang="scss">
.page {
padding: 20px;
background-color: #fff;
}
/deep/.input {
margin-bottom: 15px;
.is-input-border {
border-color: #dedede !important;
}
}
.input {
margin-bottom: 20px;
}
.code-wrap {
display: flex;
}
.code {
flex: 1;
}
.send-code {
width: 100px;
margin-left: 20px;
text-align: center;
color: #4386ff;
font-size: 12px;
line-height: 36px;
border: 1px solid #4386ff;
border-radius: 5px;
}
</style>

@ -0,0 +1,199 @@
<template>
<view>
<view class="filter">
<uni-search-bar class="search" radius="30" placeholder="请输入方案名称" clearButton="auto" cancelButton="none" @confirm="search" />
</view>
<ul class="tab">
<li v-for="(tab, i) in tabs" :class="{active: curTab === tab.id}" @click="tabChange(tab)">{{ tab.name }}</li>
</ul>
<view v-if="list.length" class="list">
<view v-for="(item, i) in list" class="item" @click="toDetail(i + 2)">
<view class="c-name">{{ item.name }}</view>
<view class="content">
<view class="info">
<view class="line">
<view class="name">产品</view>
<view class="val ell-wrap">
<view :class="{ell: !item.toggle}" v-html="item.product"></view>
<view v-if="item.product.length > 14" class="toggle" @click.stop="toggle(item)">{{ item.toggle ? '收起' : '展开' }}</view>
</view>
</view>
<view class="line">
<view class="name">更新日期</view>
<view class="val">{{ item.date }}</view>
</view>
<view class="line">
<view class="name">适用专业</view>
<view class="val">{{ item.major }}</view>
</view>
</view>
<view class="detail" @click.stop="toEmail(i)">下载</view>
</view>
</view>
</view>
<empty v-else></empty>
</view>
</template>
<script>
export default {
data() {
return {
curTab: '',
tabs: [
{
name: '不限',
id: ''
},
{
name: '标准方案',
id: 1
},
{
name: '智信云方案',
id: 2
}
],
list: [],
all: [
{
type: 1,
toggle: false,
name: '人工智能实验室建设方案-2020.1',
product: `python程序设计实验教学系统<br>
Docker技术实验教学系统<br>
JAVA语言程序设计实验教学系统<br>
大数据开发实验教学系统<br>
Openstack云计算实验教学系统`,
date: '2022-06-02',
major: '人工智能方向'
},
{
type: 1,
toggle: false,
name: '大数据管理与应用专业建设方案',
product: `python程序设计实验教学系统<br>
量化投资实验教学系统<br>
区块链数字货币交易实验系统<br>
大数分析实验教学系统<br>
数据可视化实验教学系统<br>
大数据实证科研平台<br>
全球宏观经济<br>
数据库全球金融交易数据库 `,
date: '2022-06-02',
major: '大数据方向'
},
{
type: 1,
toggle: false,
name: '金融科技实验室建设方案V2.0',
product: `python程序设计实验教学系统<br>
量化投资实验教学系统<br>
区块链数字货币交易实验教学系统<br>
大数据采集实验教学系统<br>
大数据分析实验教学系统<br>
金融建模实验教学系统<br>
互联网小贷实验教学系统<br>
互联网支付实验教学系统<br>
R语言实验教学系统<br>
供应链金融实验教学系统<br>
Python数据清洗实验教学系统<br>
数据可视化实验教学系统<br>
金融随机过程实验教学系统<br>
银行综合系统<br>
大数据实证科研平台<br>
财经数据库`,
date: '2022-06-02',
major: '金融科技,大数据方向'
}
]
}
},
onShow() {
this.list = JSON.parse(JSON.stringify(this.all))
},
methods: {
search(res) {
uni.showToast({
title: '搜索:' + res.value,
icon: 'none'
})
},
// tab
tabChange(tab) {
const { id } = tab
const { all } = this
this.curTab = id
this.list = id ? all.filter(e => e.type === id) : all
},
//
toDetail(id) {
this.$util.openFile(id)
},
//
toEmail(id) {
this.$util.to(`../send/send?id=${id}`)
},
//
toggle(item) {
item.toggle = !item.toggle
}
}
}
</script>
<style scoped lang="scss">
.list {
background-color: #fff;
.item {
padding: 20rpx 40rpx;
border-bottom: 1px solid #f1f1f1;
}
.c-name {
font-size: 30rpx;
color: #333;
}
.line {
display: flex;
padding: 10rpx 0;
}
.content {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20rpx;
}
.name {
width: 140rpx;
margin-right: 10rpx;
white-space: nowrap;
font-size: 28rpx;
color: #999;
}
.val {
max-width: 88%;
font-size: 28rpx;
color: #333;
}
.ell {
height: 38rpx;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.toggle {
margin-top: 10rpx;
white-space: nowrap;
font-size: 24rpx;
color: #0e92ef;
}
.detail {
margin-left: 20rpx;
font-size: 26rpx;
color: #1f83ff;
white-space: nowrap;
}
}
</style>

@ -0,0 +1,217 @@
<template>
<view>
<view class="filter">
<uni-search-bar class="search" radius="30" placeholder="请输入产品名称" v-model="keyword" clearButton="auto" cancelButton="none" />
<view :class="['sort', sort]" @click="switchSort"></view>
</view>
<ul class="tab">
<li v-for="(tab, i) in tabs" :class="{active: curTab === tab.id}" @click="tabChange(tab)">{{ tab.name }}</li>
</ul>
<ul class="list">
<li v-for="item in list">
<view class="pro-name">
<image class="icon" :src="$util.getIcon(item)" mode="widthFix"></image>
{{ item.productName }}
</view>
<view class="info">
<view class="line">
<text class="name">产品简介</text>
<view class="val ell-wrap">
<view :class="{ell: !item.toggle}">{{ item.briefIntroduction }}</view>
<view class="toggle" @click="toggle(item)">{{ item.toggle ? '收起' : '展开' }}</view>
</view>
</view>
<view class="line">
<text class="name">产品类型</text>
<text class="val">{{ tabs.find(e => e.id === item.productType).name }}</text>
</view>
<view class="line">
<text class="name">市场单价</text>
<text class="val">{{ item.marketPrice }}/</text>
</view>
<view class="line">
<text class="name">结算方式</text>
<text class="val">{{ item.settlementMethod ? '比例分成' : '结算单价' }}</text>
</view>
<view class="line">
<text class="name">供应厂商</text>
<text class="val">{{ item.supplier }}</text>
</view>
</view>
</li>
</ul>
<uni-load-more :status="status" />
</view>
</template>
<script>
import { AppletsDataProductList } from '@/apis/modules/product.js'
export default {
data() {
return {
curTab: '',
tabs: [
{
name: '全部',
id: ''
},
{
name: '实训课程',
id: 1
},
{
name: '理论课程',
id: 0
},
{
name: '数据产品',
id: 2
}
],
reachBottom: 0, // 0->,1->,-1->
status: 'more', // more|loading|noMore
searchTimer: null,
sort: 'desc',
keyword: '',
list: [],
page: 1,
pageSize: 10
}
},
watch: {
keyword () {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.initList()
}, 500)
}
},
//
onPullDownRefresh() {
this.initList()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1500)
},
//
onReachBottom() {
if (this.reachBottom >= 0) {
this.reachBottom = 1
this.status = 'loading'
this.getList()
}
},
onShow() {
this.initList()
},
methods: {
getList() {
AppletsDataProductList({
sort: this.sort,
keywords: this.keyword,
productType: this.curTab,
pageNum: this.page,
pageSize: this.pageSize
}).then(({ data }) => {
// list
const list = data.records
list.map(e => {
e.toggle = e.briefIntroduction.length < 14 // 14
})
this.list = this.reachBottom > 0 ? [...this.list, ...list] : list
this.page++ // page+1
const noMore = this.list.length === data.total //
this.status = noMore ? 'noMore' : 'more' // noMore
this.reachBottom = noMore ? -1 : 0 // -1
}).catch(e => {})
},
initList() {
this.page = 1
this.reachBottom = 0
this.getList()
},
//
toggle(item) {
item.toggle = !item.toggle
},
//
switchSort() {
this.sort = this.sort === 'desc' ? 'asc' : 'desc'
this.initList()
},
// tab
tabChange(tab) {
this.curTab = tab.id
this.initList()
}
}
}
</script>
<style scoped lang="scss">
.filter {
display: flex;
align-items: center;
.search {
flex: 1;
}
.sl-filter {
width: 30%;
margin-left: 10%;
}
}
.list {
li {
padding: 0 24rpx;
margin: 16rpx 24rpx;
background-color: #fff;
border-radius: 16rpx;
}
.pro-name {
display: flex;
align-items: center;
padding: 18rpx 0;
font-size: 30rpx;
color: #333;
border-bottom: 1px solid #E6E8ED;
.icon {
width: 52rpx;
margin-right: 20rpx;
}
}
.info {
padding: 12rpx 0;
}
.line {
display: flex;
padding: 12rpx 0;
}
.name {
margin-right: 10rpx;
font-size: 28rpx;
color: #999;
}
.val {
max-width: 70%;
font-size: 28rpx;
color: #333;
}
.ell-wrap {
display: inline-flex;
align-items: center;
}
.ell {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.toggle {
margin-left: 10rpx;
white-space: nowrap;
font-size: 24rpx;
color: #0e92ef;
}
}
</style>

@ -0,0 +1,108 @@
<template>
<view class="page">
<view class="wrap">
<view class="inner">
<view class="info">
<image class="avatar" :src="avatar" mode=""></image>
<view class="text">
<view class="invite">
<text class="name">{{ userName }}</text> 邀请你加入城市合伙人计划
</view>
</view>
</view>
<view class="com">{{ team.partnerClassificationName }}</view>
<u-qrcode ref="qrcode" canvas-id="qrcode" :size="size" :value="link"></u-qrcode>
<view class="tips">扫一扫加入我们吧</view>
</view>
<view class="warn">邀请二维码失效日期{{ expireTime }}</view>
</view>
</view>
</template>
<script>
import { generateInvitationCode } from '@/apis/modules/parner.js'
export default {
data() {
return {
avatar: uni.getStorageSync('avatar') || '@/static/image/avatar.png',
userName: uni.getStorageSync('userName'),
expireTime: '',
qrcode: '',
link: '',
size: uni.upx2px(420),
team: uni.getStorageSync('team')
}
},
onShow() {
this.getQrcode()
},
methods: {
//
getQrcode() {
const { team } = this
//
generateInvitationCode(uni.getStorageSync('team').accountId).then(({ expireTime }) => {
const date = new Date(Date.now() + expireTime * 1000) // *1000
this.expireTime = `${date.getFullYear()}-${this.$util.preZero(date.getMonth() + 1)}-${this.$util.preZero(date.getDate())} ${this.$util.preZero(date.getHours())}:${this.$util.preZero(date.getMinutes())}:${this.$util.preZero(date.getMinutes())}`
const team = this.team
this.link = `https://huorantech.cn/#/join?accountId=${team.accountId}&id=${team.teamId}&isTeam=0`
// this.link = `http://192.168.31.125:8086/#/join?accountId=${team.accountId}&id=${team.teamId}&isTeam=0`
}).catch(e => {})
},
}
}
</script>
<style scoped lang="scss">
.page {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
overflow: hidden;
}
.wrap {
padding-bottom: 40rpx;
text-align: center;
background-color: #fff;
.inner {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 40rpx 80rpx 0;
}
.info {
display: flex;
align-items: center;
text-align: left;
}
.avatar {
width: 80rpx;
height: 80rpx;
margin-right: 20rpx;
border-radius: 50%;
}
.invite {
font-size: 26rpx;
}
.name {
margin-right: 5rpx;
font-size: 30rpx;
color: $uni-primary;
}
.com {
margin: 20rpx 0;
font-size: 30rpx;
}
.tips {
margin: 30rpx 0 20rpx;
font-size: 24rpx;
color: #333;
}
.warn {
font-size: 24rpx;
color: #f00;
}
}
</style>

@ -0,0 +1,105 @@
<template>
<view class="container">
<uni-card :is-shadow="false" :border="false" padding="0" is-full>
<view class="form">
<view class="line">
<uni-data-checkbox v-model="myVal" :localdata="checkList" @change="myChange"></uni-data-checkbox>
<text>我的邮箱</text>
<uni-easyinput v-model="email" disabled />
<view class="bind" @click="$util.to('../email/email')">绑定</view>
</view>
<view class="line">
<uni-data-checkbox v-model="otherVal" :localdata="checkList" @change="otherChange"></uni-data-checkbox>
<text>其他邮箱</text>
<uni-easyinput v-model="otherEmail" placeholder="请输入其他邮箱" />
</view>
<button type="primary" @click="submit">确认</button>
</view>
</uni-card>
</view>
</template>
<script>
import { mailFileSend } from '@/apis/modules/parner.js'
import { my } from '@/apis/modules/parner.js'
export default {
data() {
return {
id: '',
email: '',
otherEmail: '',
myVal: 1,
otherVal: '',
checkList: [{
text: '',
value: 1
}],
files: [
'人工智能实验室建设方案-2020.1.docx',
'大数据管理与应用专业建设方案.docx',
'金融科技实验室建设方案V2.0.docx'
]
}
},
onShow() {
const pages = getCurrentPages()
const { options } = pages[pages.length - 1]
this.id = options.id
this.getInfo()
},
methods: {
//
getInfo() {
const team = uni.getStorageSync('team')
my({
partnerId: team.partnerId,
teamId: team.teamId
}).then(({ my }) => {
this.email = my.info.email
}).catch(e => {})
},
myChange(e) {
this.otherVal = ''
},
otherChange(e) {
this.myVal = ''
},
//
submit() {
const { otherEmail } = this
if (this.myVal && !this.email) return this.$util.errMsg('请选择其他邮箱!')
if (this.otherVal && !otherEmail) return this.$util.errMsg('请输入邮箱!')
mailFileSend({
copyWriting: this.files[this.id],
mail: this.myVal ? this.email : otherEmail
}).then(res => {
this.$util.sucMsg('发送成功!')
setTimeout(() => {
uni.navigateBack()
}, 1000)
}).catch(e => {})
},
}
}
</script>
<style scoped lang="scss">
.form {
padding: 15px;
background-color: #fff;
}
.line {
display: flex;
align-items: center;
margin-bottom: 15px;
text {
margin: 0 10px 0 -20px;
}
}
.bind {
margin-left: 20rpx;
font-size: 26rpx;
color: #1f83ff;
white-space: nowrap;
}
</style>

@ -0,0 +1,90 @@
<template>
<view>
<uni-list>
<uni-list-item :show-extra-icon="true" showArrow :extra-icon="phoneIcon" title="手机号" :rightText="info.phone" clickable @click="toPage('../phone/phone')" />
<uni-list-item :show-extra-icon="true" showArrow :extra-icon="mailIcon" title="邮箱" :rightText="info.email" clickable @click="toPage('../email/email')" />
<uni-list-item :show-extra-icon="true" showArrow :extra-icon="accountIcon" title="账号" :rightText="info.account" clickable @click="toPage('../account/account')" />
<uni-list-item :show-extra-icon="true" showArrow :extra-icon="pwdIcon" title="密码" rightText="******" clickable @click="toPage('../password/password')" />
</uni-list>
<view v-if="auth('我的:退出账号')" class="logout" @click="logout">退出登录</view>
</view>
</template>
<script>
import { my } from '@/apis/modules/parner.js'
export default {
data() {
return {
phoneIcon: {
color: '#007eff',
size: '22',
type: 'phone'
},
mailIcon: {
color: '#007eff',
size: '22',
type: 'email'
},
accountIcon: {
color: '#007eff',
size: '22',
type: 'person'
},
pwdIcon: {
color: '#007eff',
size: '22',
type: 'locked'
},
info: {
account: '',
phone: '',
email: ''
}
}
},
onShow() {
this.getInfo()
},
methods: {
//
getInfo() {
const team = uni.getStorageSync('team')
my({
partnerId: team.partnerId,
teamId: team.teamId
}).then(({ my }) => {
this.info = my.info
}).catch(e => {})
},
toPage(path) {
this.$util.to(path)
},
// 退
logout() {
const that = this
uni.showModal({
title: '提示',
content: '确定要退出账号吗?',
success(res) {
if (res.confirm) {
uni.clearStorageSync()
that.$util.to('../login/login')
}
}
})
}
}
}
</script>
<style scoped lang="scss">
.logout {
padding: 30rpx 0;
margin-top: 30rpx;
font-size: 28rpx;
color: #333;
text-align: center;
background-color: #fff;
}
</style>

@ -0,0 +1,162 @@
<template>
<view>
<uni-card :is-shadow="false" :border="false" is-full spacing="0" margin="20">
<uni-search-bar class="search" radius="30" placeholder="请输入成员名称" clearButton="auto" cancelButton="none" v-model="keyword" />
</uni-card>
<template v-if="list.length">
<ul class="list">
<li v-for="item in list">
<image class="avatar" :src="item.userAvatars || require('@/static/image/avatar.png')" mode=""></image>
<view class="info">
<view class="c-name">{{ item.userName }}</view>
<view class="line">
<text class="name">手机号码</text>
<text class="val">{{ item.phone }}</text>
</view>
<view class="line">
<text class="name">加入时间</text>
<text class="val">{{ item.lastLoginTime.split(' ')[0] }}</text>
</view>
</view>
</li>
</ul>
<uni-load-more :status="status" />
</template>
<empty v-else></empty>
<uni-icons v-if="auth('团队:邀请成员')" class="plus" type="plus-filled" size="60" color="#007eff" @click="$util.to('../addStaff/addStaff')"></uni-icons>
<view v-if="!per" class="per-mask">功能升级中敬请期待...</view>
</view>
</template>
<script>
import { teamList } from '@/apis/modules/parner.js'
export default {
data() {
return {
per: true, //
reachBottom: 0, // 0->,1->,-1->
status: 'more', // more|loading|noMore
searchTimer: null,
keyword: '',
list: [],
page: 1,
pageSize: 10
}
},
watch: {
keyword () {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.initList()
}, 500)
}
},
//
onPullDownRefresh() {
this.initList()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1500)
},
//
onReachBottom() {
if (this.reachBottom >= 0) {
this.reachBottom = 1
this.status = 'loading'
this.getList()
}
},
onShow() {
this.per = true
this.initRole()
},
methods: {
//
initRole() {
const auth = uni.getStorageSync('auth')
if (!auth.includes('团队')) {
this.per = false
} else if (auth.includes('团队:团队列表')) {
this.initList()
}
},
//
getList() {
uni.showLoading({
title: '加载中'
})
teamList({
type: 1,
pageNum: this.page,
pageSize: this.pageSize,
keyWord: this.keyword,
partnerClassificationId: uni.getStorageSync('team').teamId
}).then(({ pageList }) => {
uni.hideLoading()
const { records } = pageList
// list
this.list = this.reachBottom > 0 ? [...this.list, ...records] : records
this.page++ // page+1
const noMore = this.list.length === pageList.total //
this.status = noMore ? 'noMore' : 'more' // noMore
this.reachBottom = noMore ? -1 : 0 // -1
}).catch(e => {
uni.hideLoading()
})
},
initList() {
this.page = 1
this.reachBottom = 0
this.getList()
}
}
}
</script>
<style scoped lang="scss">
.list {
margin-top: 20rpx;
background-color: #fff;
li {
display: flex;
align-items: center;
padding: 20rpx 40rpx;
border-bottom: 1px solid #f7f7f7;
&:last-child {
border-bottom: 0;
}
}
.avatar {
width: 100rpx;
height: 100rpx;
margin-right: 30rpx;
border-radius: 50%;
}
.c-name {
margin-bottom: 6rpx;
font-size: 30rpx;
color: #333;
}
.line {
display: flex;
padding: 2rpx 0;
}
.name {
margin-right: 10rpx;
white-space: nowrap;
font-size: 28rpx;
color: #999;
}
.val {
font-size: 28rpx;
color: #333;
}
}
.plus {
position: fixed;
bottom: 40rpx;
right: 40rpx;
}
</style>

@ -0,0 +1,32 @@
@font-face {
font-family: "iconfont"; src: url('/static/iconfont/iconfont.ttf') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-qrcode:before {
content: "\e7dd";
}
.icon-dingdan:before {
content: "\e601";
}
.icon-product:before {
content: "\e788";
}
.icon-edit:before {
content: "\e621";
}
.icon-filter:before {
content: "\e6b9";
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

@ -0,0 +1,221 @@
button[type=primary] {
background-color: #007eff;
}
page {
height: 100%;
background-color: #f5f5f5;
}
ul {
padding-left: 0;
li {
list-style: none;
}
}
.block {
padding: 0 24rpx;
margin: 20rpx 24rpx;
border-radius: 16rpx;
background-color: #fff;
}
.l-title {
display: flex;
align-items: center;
padding: 28rpx 0;
font-size: 28rpx;
color: #333;
&:before {
content: '';
width: 6rpx;
height: 28rpx;
margin-right: 12rpx;
vertical-align: middle;
background-color: #4876F9;
}
}
.filter {
display: flex;
align-items: center;
padding: 10rpx 30rpx 10rpx 10rpx;
background-color: #fff;
.search {
flex: 1;
}
.uni-searchbar__box {
height: 70rpx;
}
.sort {
margin: 0 20rpx 0 10rpx;
&:before {
content: '';
display: block;
margin-bottom: 5rpx;
border: 15rpx solid transparent;
border-bottom-color: #B9B9B9;
}
&:after {
content: '';
display: block;
border: 15rpx solid transparent;
border-top-color: #B9B9B9;
}
&.desc:before {
border-bottom-color: #007EFF;
}
&.asc:after {
border-top-color: #007EFF;
}
}
}
.form-list {
border-top: 1px solid #E6E8ED;
.line {
display: flex;
justify-content: space-between;
align-items: center;
padding: 28rpx 0;
border-bottom: 1px solid #E6E8ED;
&:last-child {
border-bottom: 0;
}
}
.ph {
font-size: 28rpx;
color: #999;
}
.name, .val, input {
font-size: 28rpx;
color: #333;
}
input {
flex: 1;
margin-left: 20rpx;
text-align: right;
}
.req {
.name:after {
content: '*';
margin-left: 6rpx;
color: #F53232;
vertical-align: middle;
}
}
.err {
.name {
color: #f00;
}
}
.inline {
display: inline-flex;
align-items: center;
@extend input;
input {
margin-right: 10rpx;
}
}
}
.picker-input .input-value-border {
line-height: 1;
border: 0;
}
.tab {
display: flex;
justify-content: space-around;
margin-bottom: 20rpx;
background-color: #fff;
li {
padding: 0 20rpx;
font-size: 28rpx;
line-height: 100rpx;
border-bottom: 3px solid transparent;
}
.active {
color: $uni-primary;
border-bottom-color: $uni-primary;
}
}
.plus {
position: fixed;
bottom: 40rpx;
right: 40rpx;
}
.popup-mask {
z-index: 9;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, .5);
}
.popup {
z-index: 10;
position: fixed;
bottom: 0%;
width: 100%;
height: 90vh;
background-color: #fff;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
.top {
text-align: center;
line-height: 100rpx;
font-size: 28rpx;
color: #333;
border-bottom: 1px solid #f1f1f1;
}
.close {
position: absolute;
top: 30rpx;
right: 30rpx;
}
.list {
max-height: calc(90vh - 206rpx);
padding: 0 30rpx;
overflow: auto;
.item {
line-height: 80rpx;
font-size: 24rpx;
}
}
}
.btn-wrap {
z-index: 10;
position: fixed;
bottom: env(safe-area-inset-bottom);
bottom: 0;
display: flex;
justify-content: flex-end;
align-items: center;
width: 100%;
padding: 20rpx 24rpx 20rpx 40rpx;
background-color: #fff;
box-shadow: 0px 0px 7rpx 0px rgba(203, 203, 203, 0.55);
box-sizing: border-box;
.btn {
width: 100%;
line-height: 80rpx;
font-size: 30rpx;
text-align: center;
color: #fff;
border-radius: 10rpx;
background-color: #007EFF;
}
}
.per-mask {
z-index: 3;
position: fixed;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
font-size: 28rpx;
color: #333;
background-color: rgba(255, 255, 255, 0.95);
-webkit-user-drag: none;
-webkit-user-select: none;
user-select: none;
}

@ -0,0 +1,3 @@
@import '@/uni_modules/uni-scss/variables.scss';
@import '@/styles/common.scss';
@import '@/static/iconfont/iconfont.css';

@ -0,0 +1,44 @@
## 3.2.2(2022-05-12)
3.2.2
修复vue3引入js报错问题;
增加draw可选项,可在每一阶段绘制前后扩展自定义方法,详见文档draw(options);
其他优化。
## 3.2.1(2022-05-09)
3.2.1
已实现导入临时文件方法`toTempFilePath`;
已实现保存二维码到本地或相册方法`save`;
已实现生成完成回调事件`complete`;
文档补充。
## 3.2.0(2022-05-07)
3.2.0
适用所有支持canvas的前端应用和Node.js服务端;
微信小程序端切换为canvas2d;
支持绘制背景图片和前景图片,也就是说可以绘制背景和logo了;
支持对定位角进行样式设置;
支持对分割图案进行样式设置;
支持对对齐图案进行样式设置;
支持对时序图案进行样式设置;
支持对暗块进行样式设置;
支持对版本信息进行样式设置;
解决小块之间出现白线问题。
## 3.0.1(2022-01-05)
3.0.1 gcanvas引用目录调整。
## 3.0.0(2022-01-04)
3.0.0 uQRCode 3.0 全新版本来袭。
## 2.0.4(2021-11-19)
2.0.4 新增绘制模式;新增绘制延时、canvas导入文件延时属性。
## 2.0.3(2021-10-18)
2.0.3 修复在部分安卓设备生成异常;移除延迟绘制;新增批量生成示例。
## 2.0.23(2021-08-09)
## 2.0.22(2021-08-09)
## 2.0.21(2021-07-28)
## 2.0.2(2021-07-28)
2.0.2 新增延迟绘制。
## 2.0.1(2021-07-26)
2.0.1 调整为uni_modules目录规范。

@ -0,0 +1,241 @@
const isWeex = typeof WXEnvironment !== 'undefined';
const isWeexIOS = isWeex && /ios/i.test(WXEnvironment.platform);
const isWeexAndroid = isWeex && !isWeexIOS;
import GLmethod from '../context-webgl/GLmethod';
const GCanvasModule =
(typeof weex !== 'undefined' && weex.requireModule) ? (weex.requireModule('gcanvas')) :
(typeof __weex_require__ !== 'undefined') ? (__weex_require__('@weex-module/gcanvas')) : {};
let isDebugging = false;
let isComboDisabled = false;
const logCommand = (function () {
const methodQuery = [];
Object.keys(GLmethod).forEach(key => {
methodQuery[GLmethod[key]] = key;
})
const queryMethod = (id) => {
return methodQuery[parseInt(id)] || 'NotFoundMethod';
}
const logCommand = (id, cmds) => {
const mId = cmds.split(',')[0];
const mName = queryMethod(mId);
console.log(`=== callNative - componentId:${id}; method: ${mName}; cmds: ${cmds}`);
}
return logCommand;
})();
function joinArray(arr, sep) {
let res = '';
for (let i = 0; i < arr.length; i++) {
if (i !== 0) {
res += sep;
}
res += arr[i];
}
return res;
}
const commandsCache = {}
const GBridge = {
callEnable: (ref, configArray) => {
commandsCache[ref] = [];
return GCanvasModule.enable({
componentId: ref,
config: configArray
});
},
callEnableDebug: () => {
isDebugging = true;
},
callEnableDisableCombo: () => {
isComboDisabled = true;
},
callSetContextType: function (componentId, context_type) {
GCanvasModule.setContextType(context_type, componentId);
},
callReset: function(id){
GCanvasModule.resetComponent && canvasModule.resetComponent(componentId);
},
render: isWeexIOS ? function (componentId) {
return GCanvasModule.extendCallNative({
contextId: componentId,
type: 0x60000001
});
} : function (componentId) {
return callGCanvasLinkNative(componentId, 0x60000001, 'render');
},
render2d: isWeexIOS ? function (componentId, commands, callback) {
if (isDebugging) {
console.log('>>> >>> render2d ===');
console.log('>>> commands: ' + commands);
}
GCanvasModule.render([commands, callback?true:false], componentId, callback);
} : function (componentId, commands,callback) {
if (isDebugging) {
console.log('>>> >>> render2d ===');
console.log('>>> commands: ' + commands);
}
callGCanvasLinkNative(componentId, 0x20000001, commands);
if(callback){
callback();
}
},
callExtendCallNative: isWeexIOS ? function (componentId, cmdArgs) {
throw 'should not be here anymore ' + cmdArgs;
} : function (componentId, cmdArgs) {
throw 'should not be here anymore ' + cmdArgs;
},
flushNative: isWeexIOS ? function (componentId) {
const cmdArgs = joinArray(commandsCache[componentId], ';');
commandsCache[componentId] = [];
if (isDebugging) {
console.log('>>> >>> flush native ===');
console.log('>>> commands: ' + cmdArgs);
}
const result = GCanvasModule.extendCallNative({
"contextId": componentId,
"type": 0x60000000,
"args": cmdArgs
});
const res = result && result.result;
if (isDebugging) {
console.log('>>> result: ' + res);
}
return res;
} : function (componentId) {
const cmdArgs = joinArray(commandsCache[componentId], ';');
commandsCache[componentId] = [];
if (isDebugging) {
console.log('>>> >>> flush native ===');
console.log('>>> commands: ' + cmdArgs);
}
const result = callGCanvasLinkNative(componentId, 0x60000000, cmdArgs);
if (isDebugging) {
console.log('>>> result: ' + result);
}
return result;
},
callNative: function (componentId, cmdArgs, cache) {
if (isDebugging) {
logCommand(componentId, cmdArgs);
}
commandsCache[componentId].push(cmdArgs);
if (!cache || isComboDisabled) {
return GBridge.flushNative(componentId);
} else {
return undefined;
}
},
texImage2D(componentId, ...args) {
if (isWeexIOS) {
if (args.length === 6) {
const [target, level, internalformat, format, type, image] = args;
GBridge.callNative(
componentId,
GLmethod.texImage2D + ',' + 6 + ',' + target + ',' + level + ',' + internalformat + ',' + format + ',' + type + ',' + image.src
)
} else if (args.length === 9) {
const [target, level, internalformat, width, height, border, format, type, image] = args;
GBridge.callNative(
componentId,
GLmethod.texImage2D + ',' + 9 + ',' + target + ',' + level + ',' + internalformat + ',' + width + ',' + height + ',' + border + ',' +
+ format + ',' + type + ',' + (image ? image.src : 0)
)
}
} else if (isWeexAndroid) {
if (args.length === 6) {
const [target, level, internalformat, format, type, image] = args;
GCanvasModule.texImage2D(componentId, target, level, internalformat, format, type, image.src);
} else if (args.length === 9) {
const [target, level, internalformat, width, height, border, format, type, image] = args;
GCanvasModule.texImage2D(componentId, target, level, internalformat, width, height, border, format, type, (image ? image.src : 0));
}
}
},
texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image) {
if (isWeexIOS) {
if (arguments.length === 8) {
GBridge.callNative(
componentId,
GLmethod.texSubImage2D + ',' + 6 + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset, + ',' + format + ',' + type + ',' + image.src
)
}
} else if (isWeexAndroid) {
GCanvasModule.texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image.src);
}
},
bindImageTexture(componentId, src, imageId) {
GCanvasModule.bindImageTexture([src, imageId], componentId);
},
perloadImage([url, id], callback) {
GCanvasModule.preLoadImage([url, id], function (image) {
image.url = url;
image.id = id;
callback(image);
});
},
measureText(text, fontStyle, componentId) {
return GCanvasModule.measureText([text, fontStyle], componentId);
},
getImageData (componentId, x, y, w, h, callback) {
GCanvasModule.getImageData([x, y,w,h],componentId,callback);
},
putImageData (componentId, data, x, y, w, h, callback) {
GCanvasModule.putImageData([x, y,w,h,data],componentId,callback);
},
toTempFilePath(componentId, x, y, width, height, destWidth, destHeight, fileType, quality, callback){
GCanvasModule.toTempFilePath([x, y, width,height, destWidth, destHeight, fileType, quality], componentId, callback);
}
}
export default GBridge;

@ -0,0 +1,18 @@
class FillStyleLinearGradient {
constructor(x0, y0, x1, y1) {
this._start_pos = { _x: x0, _y: y0 };
this._end_pos = { _x: x1, _y: y1 };
this._stop_count = 0;
this._stops = [0, 0, 0, 0, 0];
}
addColorStop = function (pos, color) {
if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) {
this._stops[this._stop_count] = { _pos: pos, _color: color };
this._stop_count++;
}
}
}
export default FillStyleLinearGradient;

@ -0,0 +1,8 @@
class FillStylePattern {
constructor(img, pattern) {
this._style = pattern;
this._img = img;
}
}
export default FillStylePattern;

@ -0,0 +1,17 @@
class FillStyleRadialGradient {
constructor(x0, y0, r0, x1, y1, r1) {
this._start_pos = { _x: x0, _y: y0, _r: r0 };
this._end_pos = { _x: x1, _y: y1, _r: r1 };
this._stop_count = 0;
this._stops = [0, 0, 0, 0, 0];
}
addColorStop(pos, color) {
if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) {
this._stops[this._stop_count] = { _pos: pos, _color: color };
this._stop_count++;
}
}
}
export default FillStyleRadialGradient;

@ -0,0 +1,666 @@
import FillStylePattern from './FillStylePattern';
import FillStyleLinearGradient from './FillStyleLinearGradient';
import FillStyleRadialGradient from './FillStyleRadialGradient';
import GImage from '../env/image.js';
import {
ArrayBufferToBase64,
Base64ToUint8ClampedArray
} from '../env/tool.js';
export default class CanvasRenderingContext2D {
_drawCommands = '';
_globalAlpha = 1.0;
_fillStyle = 'rgb(0,0,0)';
_strokeStyle = 'rgb(0,0,0)';
_lineWidth = 1;
_lineCap = 'butt';
_lineJoin = 'miter';
_miterLimit = 10;
_globalCompositeOperation = 'source-over';
_textAlign = 'start';
_textBaseline = 'alphabetic';
_font = '10px sans-serif';
_savedGlobalAlpha = [];
timer = null;
componentId = null;
_notCommitDrawImageCache = [];
_needRedrawImageCache = [];
_redrawCommands = '';
_autoSaveContext = true;
// _imageMap = new GHashMap();
// _textureMap = new GHashMap();
constructor() {
this.className = 'CanvasRenderingContext2D';
//this.save()
}
setFillStyle(value) {
this.fillStyle = value;
}
set fillStyle(value) {
this._fillStyle = value;
if (typeof(value) == 'string') {
this._drawCommands = this._drawCommands.concat("F" + value + ";");
} else if (value instanceof FillStylePattern) {
const image = value._img;
if (!image.complete) {
image.onload = () => {
var index = this._needRedrawImageCache.indexOf(image);
if (index > -1) {
this._needRedrawImageCache.splice(index, 1);
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._redrawflush(true);
}
}
this._notCommitDrawImageCache.push(image);
} else {
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
}
//CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
} else if (value instanceof FillStyleLinearGradient) {
var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
} else if (value instanceof FillStyleRadialGradient) {
var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + value._end_pos._r.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
}
}
get fillStyle() {
return this._fillStyle;
}
get globalAlpha() {
return this._globalAlpha;
}
setGlobalAlpha(value) {
this.globalAlpha = value;
}
set globalAlpha(value) {
this._globalAlpha = value;
this._drawCommands = this._drawCommands.concat("a" + value.toFixed(2) + ";");
}
get strokeStyle() {
return this._strokeStyle;
}
setStrokeStyle(value) {
this.strokeStyle = value;
}
set strokeStyle(value) {
this._strokeStyle = value;
if (typeof(value) == 'string') {
this._drawCommands = this._drawCommands.concat("S" + value + ";");
} else if (value instanceof FillStylePattern) {
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
} else if (value instanceof FillStyleLinearGradient) {
var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
} else if (value instanceof FillStyleRadialGradient) {
var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y + ",".toFixed(2) + value._end_pos._r.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
}
}
get lineWidth() {
return this._lineWidth;
}
setLineWidth(value) {
this.lineWidth = value;
}
set lineWidth(value) {
this._lineWidth = value;
this._drawCommands = this._drawCommands.concat("W" + value + ";");
}
get lineCap() {
return this._lineCap;
}
setLineCap(value) {
this.lineCap = value;
}
set lineCap(value) {
this._lineCap = value;
this._drawCommands = this._drawCommands.concat("C" + value + ";");
}
get lineJoin() {
return this._lineJoin;
}
setLineJoin(value) {
this.lineJoin = value
}
set lineJoin(value) {
this._lineJoin = value;
this._drawCommands = this._drawCommands.concat("J" + value + ";");
}
get miterLimit() {
return this._miterLimit;
}
setMiterLimit(value) {
this.miterLimit = value
}
set miterLimit(value) {
this._miterLimit = value;
this._drawCommands = this._drawCommands.concat("M" + value + ";");
}
get globalCompositeOperation() {
return this._globalCompositeOperation;
}
set globalCompositeOperation(value) {
this._globalCompositeOperation = value;
let mode = 0;
switch (value) {
case "source-over":
mode = 0;
break;
case "source-atop":
mode = 5;
break;
case "source-in":
mode = 0;
break;
case "source-out":
mode = 2;
break;
case "destination-over":
mode = 4;
break;
case "destination-atop":
mode = 4;
break;
case "destination-in":
mode = 4;
break;
case "destination-out":
mode = 3;
break;
case "lighter":
mode = 1;
break;
case "copy":
mode = 2;
break;
case "xor":
mode = 6;
break;
default:
mode = 0;
}
this._drawCommands = this._drawCommands.concat("B" + mode + ";");
}
get textAlign() {
return this._textAlign;
}
setTextAlign(value) {
this.textAlign = value
}
set textAlign(value) {
this._textAlign = value;
let Align = 0;
switch (value) {
case "start":
Align = 0;
break;
case "end":
Align = 1;
break;
case "left":
Align = 2;
break;
case "center":
Align = 3;
break;
case "right":
Align = 4;
break;
default:
Align = 0;
}
this._drawCommands = this._drawCommands.concat("A" + Align + ";");
}
get textBaseline() {
return this._textBaseline;
}
setTextBaseline(value) {
this.textBaseline = value
}
set textBaseline(value) {
this._textBaseline = value;
let baseline = 0;
switch (value) {
case "alphabetic":
baseline = 0;
break;
case "middle":
baseline = 1;
break;
case "top":
baseline = 2;
break;
case "hanging":
baseline = 3;
break;
case "bottom":
baseline = 4;
break;
case "ideographic":
baseline = 5;
break;
default:
baseline = 0;
break;
}
this._drawCommands = this._drawCommands.concat("E" + baseline + ";");
}
get font() {
return this._font;
}
setFontSize(size) {
var str = this._font;
var strs = str.trim().split(/\s+/);
for (var i = 0; i < strs.length; i++) {
var values = ["normal", "italic", "oblique", "normal", "small-caps", "normal", "bold",
"bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900",
"normal", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
"semi-expanded", "expanded", "extra-expanded", "ultra-expanded"
];
if (-1 == values.indexOf(strs[i].trim())) {
if (typeof size === 'string') {
strs[i] = size;
} else if (typeof size === 'number') {
strs[i] = String(size) + 'px';
}
break;
}
}
this.font = strs.join(" ");
}
set font(value) {
this._font = value;
this._drawCommands = this._drawCommands.concat("j" + value + ";");
}
setTransform(a, b, c, d, tx, ty) {
this._drawCommands = this._drawCommands.concat("t" +
(a === 1 ? "1" : a.toFixed(2)) + "," +
(b === 0 ? "0" : b.toFixed(2)) + "," +
(c === 0 ? "0" : c.toFixed(2)) + "," +
(d === 1 ? "1" : d.toFixed(2)) + "," + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
}
transform(a, b, c, d, tx, ty) {
this._drawCommands = this._drawCommands.concat("f" +
(a === 1 ? "1" : a.toFixed(2)) + "," +
(b === 0 ? "0" : b.toFixed(2)) + "," +
(c === 0 ? "0" : c.toFixed(2)) + "," +
(d === 1 ? "1" : d.toFixed(2)) + "," + tx + "," + ty + ";");
}
resetTransform() {
this._drawCommands = this._drawCommands.concat("m;");
}
scale(a, d) {
this._drawCommands = this._drawCommands.concat("k" + a.toFixed(2) + "," +
d.toFixed(2) + ";");
}
rotate(angle) {
this._drawCommands = this._drawCommands
.concat("r" + angle.toFixed(6) + ";");
}
translate(tx, ty) {
this._drawCommands = this._drawCommands.concat("l" + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
}
save() {
this._savedGlobalAlpha.push(this._globalAlpha);
this._drawCommands = this._drawCommands.concat("v;");
}
restore() {
this._drawCommands = this._drawCommands.concat("e;");
this._globalAlpha = this._savedGlobalAlpha.pop();
}
createPattern(img, pattern) {
if (typeof img === 'string') {
var imgObj = new GImage();
imgObj.src = img;
img = imgObj;
}
return new FillStylePattern(img, pattern);
}
createLinearGradient(x0, y0, x1, y1) {
return new FillStyleLinearGradient(x0, y0, x1, y1);
}
createRadialGradient = function(x0, y0, r0, x1, y1, r1) {
return new FillStyleRadialGradient(x0, y0, r0, x1, y1, r1);
};
createCircularGradient = function(x0, y0, r0) {
return new FillStyleRadialGradient(x0, y0, 0, x0, y0, r0);
};
strokeRect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("s" + x + "," + y + "," + w + "," + h + ";");
}
clearRect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("c" + x + "," + y + "," + w +
"," + h + ";");
}
clip() {
this._drawCommands = this._drawCommands.concat("p;");
}
resetClip() {
this._drawCommands = this._drawCommands.concat("q;");
}
closePath() {
this._drawCommands = this._drawCommands.concat("o;");
}
moveTo(x, y) {
this._drawCommands = this._drawCommands.concat("g" + x.toFixed(2) + "," + y.toFixed(2) + ";");
}
lineTo(x, y) {
this._drawCommands = this._drawCommands.concat("i" + x.toFixed(2) + "," + y.toFixed(2) + ";");
}
quadraticCurveTo = function(cpx, cpy, x, y) {
this._drawCommands = this._drawCommands.concat("u" + cpx + "," + cpy + "," + x + "," + y + ";");
}
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, ) {
this._drawCommands = this._drawCommands.concat(
"z" + cp1x.toFixed(2) + "," + cp1y.toFixed(2) + "," + cp2x.toFixed(2) + "," + cp2y.toFixed(2) + "," +
x.toFixed(2) + "," + y.toFixed(2) + ";");
}
arcTo(x1, y1, x2, y2, radius) {
this._drawCommands = this._drawCommands.concat("h" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + radius + ";");
}
beginPath() {
this._drawCommands = this._drawCommands.concat("b;");
}
fillRect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("n" + x + "," + y + "," + w +
"," + h + ";");
}
rect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("w" + x + "," + y + "," + w + "," + h + ";");
}
fill() {
this._drawCommands = this._drawCommands.concat("L;");
}
stroke(path) {
this._drawCommands = this._drawCommands.concat("x;");
}
arc(x, y, radius, startAngle, endAngle, anticlockwise) {
let ianticlockwise = 0;
if (anticlockwise) {
ianticlockwise = 1;
}
this._drawCommands = this._drawCommands.concat(
"y" + x.toFixed(2) + "," + y.toFixed(2) + "," +
radius.toFixed(2) + "," + startAngle + "," + endAngle + "," + ianticlockwise +
";"
);
}
fillText(text, x, y) {
let tmptext = text.replace(/!/g, "!!");
tmptext = tmptext.replace(/,/g, "!,");
tmptext = tmptext.replace(/;/g, "!;");
this._drawCommands = this._drawCommands.concat("T" + tmptext + "," + x + "," + y + ",0.0;");
}
strokeText = function(text, x, y) {
let tmptext = text.replace(/!/g, "!!");
tmptext = tmptext.replace(/,/g, "!,");
tmptext = tmptext.replace(/;/g, "!;");
this._drawCommands = this._drawCommands.concat("U" + tmptext + "," + x + "," + y + ",0.0;");
}
measureText(text) {
return CanvasRenderingContext2D.GBridge.measureText(text, this.font, this.componentId);
}
isPointInPath = function(x, y) {
throw new Error('GCanvas not supported yet');
}
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
if (typeof image === 'string') {
var imgObj = new GImage();
imgObj.src = image;
image = imgObj;
}
if (image instanceof GImage) {
if (!image.complete) {
imgObj.onload = () => {
var index = this._needRedrawImageCache.indexOf(image);
if (index > -1) {
this._needRedrawImageCache.splice(index, 1);
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._redrawflush(true);
}
}
this._notCommitDrawImageCache.push(image);
} else {
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
}
var srcArgs = [image, sx, sy, sw, sh, dx, dy, dw, dh];
var args = [];
for (var arg in srcArgs) {
if (typeof(srcArgs[arg]) != 'undefined') {
args.push(srcArgs[arg]);
}
}
this.__drawImage.apply(this, args);
//this.__drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh);
}
}
__drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
const numArgs = arguments.length;
function drawImageCommands() {
if (numArgs === 3) {
const x = parseFloat(sx) || 0.0;
const y = parseFloat(sy) || 0.0;
return ("d" + image._id + ",0,0," +
image.width + "," + image.height + "," +
x + "," + y + "," + image.width + "," + image.height + ";");
} else if (numArgs === 5) {
const x = parseFloat(sx) || 0.0;
const y = parseFloat(sy) || 0.0;
const width = parseInt(sw) || image.width;
const height = parseInt(sh) || image.height;
return ("d" + image._id + ",0,0," +
image.width + "," + image.height + "," +
x + "," + y + "," + width + "," + height + ";");
} else if (numArgs === 9) {
sx = parseFloat(sx) || 0.0;
sy = parseFloat(sy) || 0.0;
sw = parseInt(sw) || image.width;
sh = parseInt(sh) || image.height;
dx = parseFloat(dx) || 0.0;
dy = parseFloat(dy) || 0.0;
dw = parseInt(dw) || image.width;
dh = parseInt(dh) || image.height;
return ("d" + image._id + "," +
sx + "," + sy + "," + sw + "," + sh + "," +
dx + "," + dy + "," + dw + "," + dh + ";");
}
}
this._drawCommands += drawImageCommands();
}
_flush(reserve, callback) {
const commands = this._drawCommands;
this._drawCommands = '';
CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
this._needRender = false;
}
_redrawflush(reserve, callback) {
const commands = this._redrawCommands;
CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
if (this._needRedrawImageCache.length == 0) {
this._redrawCommands = '';
}
}
draw(reserve, callback) {
if (!reserve) {
this._globalAlpha = this._savedGlobalAlpha.pop();
this._savedGlobalAlpha.push(this._globalAlpha);
this._redrawCommands = this._drawCommands;
this._needRedrawImageCache = this._notCommitDrawImageCache;
if (this._autoSaveContext) {
this._drawCommands = ("v;" + this._drawCommands);
this._autoSaveContext = false;
} else {
this._drawCommands = ("e;X;v;" + this._drawCommands);
}
} else {
this._needRedrawImageCache = this._needRedrawImageCache.concat(this._notCommitDrawImageCache);
this._redrawCommands += this._drawCommands;
if (this._autoSaveContext) {
this._drawCommands = ("v;" + this._drawCommands);
this._autoSaveContext = false;
}
}
this._notCommitDrawImageCache = [];
if (this._flush) {
this._flush(reserve, callback);
}
}
getImageData(x, y, w, h, callback) {
CanvasRenderingContext2D.GBridge.getImageData(this.componentId, x, y, w, h, function(res) {
res.data = Base64ToUint8ClampedArray(res.data);
if (typeof(callback) == 'function') {
callback(res);
}
});
}
putImageData(data, x, y, w, h, callback) {
if (data instanceof Uint8ClampedArray) {
data = ArrayBufferToBase64(data);
CanvasRenderingContext2D.GBridge.putImageData(this.componentId, data, x, y, w, h, function(res) {
if (typeof(callback) == 'function') {
callback(res);
}
});
}
}
toTempFilePath(x, y, width, height, destWidth, destHeight, fileType, quality, callback) {
CanvasRenderingContext2D.GBridge.toTempFilePath(this.componentId, x, y, width, height, destWidth, destHeight,
fileType, quality,
function(res) {
if (typeof(callback) == 'function') {
callback(res);
}
});
}
}

@ -0,0 +1,11 @@
export default class WebGLActiveInfo {
className = 'WebGLActiveInfo';
constructor({
type, name, size
}) {
this.type = type;
this.name = name;
this.size = size;
}
}

@ -0,0 +1,21 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLBuffer';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLBuffer {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}

@ -0,0 +1,21 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLFrameBuffer';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLFramebuffer {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}

@ -0,0 +1,298 @@
export default {
"DEPTH_BUFFER_BIT": 256,
"STENCIL_BUFFER_BIT": 1024,
"COLOR_BUFFER_BIT": 16384,
"POINTS": 0,
"LINES": 1,
"LINE_LOOP": 2,
"LINE_STRIP": 3,
"TRIANGLES": 4,
"TRIANGLE_STRIP": 5,
"TRIANGLE_FAN": 6,
"ZERO": 0,
"ONE": 1,
"SRC_COLOR": 768,
"ONE_MINUS_SRC_COLOR": 769,
"SRC_ALPHA": 770,
"ONE_MINUS_SRC_ALPHA": 771,
"DST_ALPHA": 772,
"ONE_MINUS_DST_ALPHA": 773,
"DST_COLOR": 774,
"ONE_MINUS_DST_COLOR": 775,
"SRC_ALPHA_SATURATE": 776,
"FUNC_ADD": 32774,
"BLEND_EQUATION": 32777,
"BLEND_EQUATION_RGB": 32777,
"BLEND_EQUATION_ALPHA": 34877,
"FUNC_SUBTRACT": 32778,
"FUNC_REVERSE_SUBTRACT": 32779,
"BLEND_DST_RGB": 32968,
"BLEND_SRC_RGB": 32969,
"BLEND_DST_ALPHA": 32970,
"BLEND_SRC_ALPHA": 32971,
"CONSTANT_COLOR": 32769,
"ONE_MINUS_CONSTANT_COLOR": 32770,
"CONSTANT_ALPHA": 32771,
"ONE_MINUS_CONSTANT_ALPHA": 32772,
"BLEND_COLOR": 32773,
"ARRAY_BUFFER": 34962,
"ELEMENT_ARRAY_BUFFER": 34963,
"ARRAY_BUFFER_BINDING": 34964,
"ELEMENT_ARRAY_BUFFER_BINDING": 34965,
"STREAM_DRAW": 35040,
"STATIC_DRAW": 35044,
"DYNAMIC_DRAW": 35048,
"BUFFER_SIZE": 34660,
"BUFFER_USAGE": 34661,
"CURRENT_VERTEX_ATTRIB": 34342,
"FRONT": 1028,
"BACK": 1029,
"FRONT_AND_BACK": 1032,
"TEXTURE_2D": 3553,
"CULL_FACE": 2884,
"BLEND": 3042,
"DITHER": 3024,
"STENCIL_TEST": 2960,
"DEPTH_TEST": 2929,
"SCISSOR_TEST": 3089,
"POLYGON_OFFSET_FILL": 32823,
"SAMPLE_ALPHA_TO_COVERAGE": 32926,
"SAMPLE_COVERAGE": 32928,
"NO_ERROR": 0,
"INVALID_ENUM": 1280,
"INVALID_VALUE": 1281,
"INVALID_OPERATION": 1282,
"OUT_OF_MEMORY": 1285,
"CW": 2304,
"CCW": 2305,
"LINE_WIDTH": 2849,
"ALIASED_POINT_SIZE_RANGE": 33901,
"ALIASED_LINE_WIDTH_RANGE": 33902,
"CULL_FACE_MODE": 2885,
"FRONT_FACE": 2886,
"DEPTH_RANGE": 2928,
"DEPTH_WRITEMASK": 2930,
"DEPTH_CLEAR_VALUE": 2931,
"DEPTH_FUNC": 2932,
"STENCIL_CLEAR_VALUE": 2961,
"STENCIL_FUNC": 2962,
"STENCIL_FAIL": 2964,
"STENCIL_PASS_DEPTH_FAIL": 2965,
"STENCIL_PASS_DEPTH_PASS": 2966,
"STENCIL_REF": 2967,
"STENCIL_VALUE_MASK": 2963,
"STENCIL_WRITEMASK": 2968,
"STENCIL_BACK_FUNC": 34816,
"STENCIL_BACK_FAIL": 34817,
"STENCIL_BACK_PASS_DEPTH_FAIL": 34818,
"STENCIL_BACK_PASS_DEPTH_PASS": 34819,
"STENCIL_BACK_REF": 36003,
"STENCIL_BACK_VALUE_MASK": 36004,
"STENCIL_BACK_WRITEMASK": 36005,
"VIEWPORT": 2978,
"SCISSOR_BOX": 3088,
"COLOR_CLEAR_VALUE": 3106,
"COLOR_WRITEMASK": 3107,
"UNPACK_ALIGNMENT": 3317,
"PACK_ALIGNMENT": 3333,
"MAX_TEXTURE_SIZE": 3379,
"MAX_VIEWPORT_DIMS": 3386,
"SUBPIXEL_BITS": 3408,
"RED_BITS": 3410,
"GREEN_BITS": 3411,
"BLUE_BITS": 3412,
"ALPHA_BITS": 3413,
"DEPTH_BITS": 3414,
"STENCIL_BITS": 3415,
"POLYGON_OFFSET_UNITS": 10752,
"POLYGON_OFFSET_FACTOR": 32824,
"TEXTURE_BINDING_2D": 32873,
"SAMPLE_BUFFERS": 32936,
"SAMPLES": 32937,
"SAMPLE_COVERAGE_VALUE": 32938,
"SAMPLE_COVERAGE_INVERT": 32939,
"COMPRESSED_TEXTURE_FORMATS": 34467,
"DONT_CARE": 4352,
"FASTEST": 4353,
"NICEST": 4354,
"GENERATE_MIPMAP_HINT": 33170,
"BYTE": 5120,
"UNSIGNED_BYTE": 5121,
"SHORT": 5122,
"UNSIGNED_SHORT": 5123,
"INT": 5124,
"UNSIGNED_INT": 5125,
"FLOAT": 5126,
"DEPTH_COMPONENT": 6402,
"ALPHA": 6406,
"RGB": 6407,
"RGBA": 6408,
"LUMINANCE": 6409,
"LUMINANCE_ALPHA": 6410,
"UNSIGNED_SHORT_4_4_4_4": 32819,
"UNSIGNED_SHORT_5_5_5_1": 32820,
"UNSIGNED_SHORT_5_6_5": 33635,
"FRAGMENT_SHADER": 35632,
"VERTEX_SHADER": 35633,
"MAX_VERTEX_ATTRIBS": 34921,
"MAX_VERTEX_UNIFORM_VECTORS": 36347,
"MAX_VARYING_VECTORS": 36348,
"MAX_COMBINED_TEXTURE_IMAGE_UNITS": 35661,
"MAX_VERTEX_TEXTURE_IMAGE_UNITS": 35660,
"MAX_TEXTURE_IMAGE_UNITS": 34930,
"MAX_FRAGMENT_UNIFORM_VECTORS": 36349,
"SHADER_TYPE": 35663,
"DELETE_STATUS": 35712,
"LINK_STATUS": 35714,
"VALIDATE_STATUS": 35715,
"ATTACHED_SHADERS": 35717,
"ACTIVE_UNIFORMS": 35718,
"ACTIVE_ATTRIBUTES": 35721,
"SHADING_LANGUAGE_VERSION": 35724,
"CURRENT_PROGRAM": 35725,
"NEVER": 512,
"LESS": 513,
"EQUAL": 514,
"LEQUAL": 515,
"GREATER": 516,
"NOTEQUAL": 517,
"GEQUAL": 518,
"ALWAYS": 519,
"KEEP": 7680,
"REPLACE": 7681,
"INCR": 7682,
"DECR": 7683,
"INVERT": 5386,
"INCR_WRAP": 34055,
"DECR_WRAP": 34056,
"VENDOR": 7936,
"RENDERER": 7937,
"VERSION": 7938,
"NEAREST": 9728,
"LINEAR": 9729,
"NEAREST_MIPMAP_NEAREST": 9984,
"LINEAR_MIPMAP_NEAREST": 9985,
"NEAREST_MIPMAP_LINEAR": 9986,
"LINEAR_MIPMAP_LINEAR": 9987,
"TEXTURE_MAG_FILTER": 10240,
"TEXTURE_MIN_FILTER": 10241,
"TEXTURE_WRAP_S": 10242,
"TEXTURE_WRAP_T": 10243,
"TEXTURE": 5890,
"TEXTURE_CUBE_MAP": 34067,
"TEXTURE_BINDING_CUBE_MAP": 34068,
"TEXTURE_CUBE_MAP_POSITIVE_X": 34069,
"TEXTURE_CUBE_MAP_NEGATIVE_X": 34070,
"TEXTURE_CUBE_MAP_POSITIVE_Y": 34071,
"TEXTURE_CUBE_MAP_NEGATIVE_Y": 34072,
"TEXTURE_CUBE_MAP_POSITIVE_Z": 34073,
"TEXTURE_CUBE_MAP_NEGATIVE_Z": 34074,
"MAX_CUBE_MAP_TEXTURE_SIZE": 34076,
"TEXTURE0": 33984,
"TEXTURE1": 33985,
"TEXTURE2": 33986,
"TEXTURE3": 33987,
"TEXTURE4": 33988,
"TEXTURE5": 33989,
"TEXTURE6": 33990,
"TEXTURE7": 33991,
"TEXTURE8": 33992,
"TEXTURE9": 33993,
"TEXTURE10": 33994,
"TEXTURE11": 33995,
"TEXTURE12": 33996,
"TEXTURE13": 33997,
"TEXTURE14": 33998,
"TEXTURE15": 33999,
"TEXTURE16": 34000,
"TEXTURE17": 34001,
"TEXTURE18": 34002,
"TEXTURE19": 34003,
"TEXTURE20": 34004,
"TEXTURE21": 34005,
"TEXTURE22": 34006,
"TEXTURE23": 34007,
"TEXTURE24": 34008,
"TEXTURE25": 34009,
"TEXTURE26": 34010,
"TEXTURE27": 34011,
"TEXTURE28": 34012,
"TEXTURE29": 34013,
"TEXTURE30": 34014,
"TEXTURE31": 34015,
"ACTIVE_TEXTURE": 34016,
"REPEAT": 10497,
"CLAMP_TO_EDGE": 33071,
"MIRRORED_REPEAT": 33648,
"FLOAT_VEC2": 35664,
"FLOAT_VEC3": 35665,
"FLOAT_VEC4": 35666,
"INT_VEC2": 35667,
"INT_VEC3": 35668,
"INT_VEC4": 35669,
"BOOL": 35670,
"BOOL_VEC2": 35671,
"BOOL_VEC3": 35672,
"BOOL_VEC4": 35673,
"FLOAT_MAT2": 35674,
"FLOAT_MAT3": 35675,
"FLOAT_MAT4": 35676,
"SAMPLER_2D": 35678,
"SAMPLER_CUBE": 35680,
"VERTEX_ATTRIB_ARRAY_ENABLED": 34338,
"VERTEX_ATTRIB_ARRAY_SIZE": 34339,
"VERTEX_ATTRIB_ARRAY_STRIDE": 34340,
"VERTEX_ATTRIB_ARRAY_TYPE": 34341,
"VERTEX_ATTRIB_ARRAY_NORMALIZED": 34922,
"VERTEX_ATTRIB_ARRAY_POINTER": 34373,
"VERTEX_ATTRIB_ARRAY_BUFFER_BINDING": 34975,
"IMPLEMENTATION_COLOR_READ_TYPE": 35738,
"IMPLEMENTATION_COLOR_READ_FORMAT": 35739,
"COMPILE_STATUS": 35713,
"LOW_FLOAT": 36336,
"MEDIUM_FLOAT": 36337,
"HIGH_FLOAT": 36338,
"LOW_INT": 36339,
"MEDIUM_INT": 36340,
"HIGH_INT": 36341,
"FRAMEBUFFER": 36160,
"RENDERBUFFER": 36161,
"RGBA4": 32854,
"RGB5_A1": 32855,
"RGB565": 36194,
"DEPTH_COMPONENT16": 33189,
"STENCIL_INDEX8": 36168,
"DEPTH_STENCIL": 34041,
"RENDERBUFFER_WIDTH": 36162,
"RENDERBUFFER_HEIGHT": 36163,
"RENDERBUFFER_INTERNAL_FORMAT": 36164,
"RENDERBUFFER_RED_SIZE": 36176,
"RENDERBUFFER_GREEN_SIZE": 36177,
"RENDERBUFFER_BLUE_SIZE": 36178,
"RENDERBUFFER_ALPHA_SIZE": 36179,
"RENDERBUFFER_DEPTH_SIZE": 36180,
"RENDERBUFFER_STENCIL_SIZE": 36181,
"FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE": 36048,
"FRAMEBUFFER_ATTACHMENT_OBJECT_NAME": 36049,
"FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL": 36050,
"FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE": 36051,
"COLOR_ATTACHMENT0": 36064,
"DEPTH_ATTACHMENT": 36096,
"STENCIL_ATTACHMENT": 36128,
"DEPTH_STENCIL_ATTACHMENT": 33306,
"NONE": 0,
"FRAMEBUFFER_COMPLETE": 36053,
"FRAMEBUFFER_INCOMPLETE_ATTACHMENT": 36054,
"FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT": 36055,
"FRAMEBUFFER_INCOMPLETE_DIMENSIONS": 36057,
"FRAMEBUFFER_UNSUPPORTED": 36061,
"FRAMEBUFFER_BINDING": 36006,
"RENDERBUFFER_BINDING": 36007,
"MAX_RENDERBUFFER_SIZE": 34024,
"INVALID_FRAMEBUFFER_OPERATION": 1286,
"UNPACK_FLIP_Y_WEBGL": 37440,
"UNPACK_PREMULTIPLY_ALPHA_WEBGL": 37441,
"CONTEXT_LOST_WEBGL": 37442,
"UNPACK_COLORSPACE_CONVERSION_WEBGL": 37443,
"BROWSER_DEFAULT_WEBGL": 37444
};

@ -0,0 +1,142 @@
let i = 1;
const GLmethod = {};
GLmethod.activeTexture = i++; //1
GLmethod.attachShader = i++;
GLmethod.bindAttribLocation = i++;
GLmethod.bindBuffer = i++;
GLmethod.bindFramebuffer = i++;
GLmethod.bindRenderbuffer = i++;
GLmethod.bindTexture = i++;
GLmethod.blendColor = i++;
GLmethod.blendEquation = i++;
GLmethod.blendEquationSeparate = i++; //10
GLmethod.blendFunc = i++;
GLmethod.blendFuncSeparate = i++;
GLmethod.bufferData = i++;
GLmethod.bufferSubData = i++;
GLmethod.checkFramebufferStatus = i++;
GLmethod.clear = i++;
GLmethod.clearColor = i++;
GLmethod.clearDepth = i++;
GLmethod.clearStencil = i++;
GLmethod.colorMask = i++; //20
GLmethod.compileShader = i++;
GLmethod.compressedTexImage2D = i++;
GLmethod.compressedTexSubImage2D = i++;
GLmethod.copyTexImage2D = i++;
GLmethod.copyTexSubImage2D = i++;
GLmethod.createBuffer = i++;
GLmethod.createFramebuffer = i++;
GLmethod.createProgram = i++;
GLmethod.createRenderbuffer = i++;
GLmethod.createShader = i++; //30
GLmethod.createTexture = i++;
GLmethod.cullFace = i++;
GLmethod.deleteBuffer = i++;
GLmethod.deleteFramebuffer = i++;
GLmethod.deleteProgram = i++;
GLmethod.deleteRenderbuffer = i++;
GLmethod.deleteShader = i++;
GLmethod.deleteTexture = i++;
GLmethod.depthFunc = i++;
GLmethod.depthMask = i++; //40
GLmethod.depthRange = i++;
GLmethod.detachShader = i++;
GLmethod.disable = i++;
GLmethod.disableVertexAttribArray = i++;
GLmethod.drawArrays = i++;
GLmethod.drawArraysInstancedANGLE = i++;
GLmethod.drawElements = i++;
GLmethod.drawElementsInstancedANGLE = i++;
GLmethod.enable = i++;
GLmethod.enableVertexAttribArray = i++; //50
GLmethod.flush = i++;
GLmethod.framebufferRenderbuffer = i++;
GLmethod.framebufferTexture2D = i++;
GLmethod.frontFace = i++;
GLmethod.generateMipmap = i++;
GLmethod.getActiveAttrib = i++;
GLmethod.getActiveUniform = i++;
GLmethod.getAttachedShaders = i++;
GLmethod.getAttribLocation = i++;
GLmethod.getBufferParameter = i++; //60
GLmethod.getContextAttributes = i++;
GLmethod.getError = i++;
GLmethod.getExtension = i++;
GLmethod.getFramebufferAttachmentParameter = i++;
GLmethod.getParameter = i++;
GLmethod.getProgramInfoLog = i++;
GLmethod.getProgramParameter = i++;
GLmethod.getRenderbufferParameter = i++;
GLmethod.getShaderInfoLog = i++;
GLmethod.getShaderParameter = i++; //70
GLmethod.getShaderPrecisionFormat = i++;
GLmethod.getShaderSource = i++;
GLmethod.getSupportedExtensions = i++;
GLmethod.getTexParameter = i++;
GLmethod.getUniform = i++;
GLmethod.getUniformLocation = i++;
GLmethod.getVertexAttrib = i++;
GLmethod.getVertexAttribOffset = i++;
GLmethod.isBuffer = i++;
GLmethod.isContextLost = i++; //80
GLmethod.isEnabled = i++;
GLmethod.isFramebuffer = i++;
GLmethod.isProgram = i++;
GLmethod.isRenderbuffer = i++;
GLmethod.isShader = i++;
GLmethod.isTexture = i++;
GLmethod.lineWidth = i++;
GLmethod.linkProgram = i++;
GLmethod.pixelStorei = i++;
GLmethod.polygonOffset = i++; //90
GLmethod.readPixels = i++;
GLmethod.renderbufferStorage = i++;
GLmethod.sampleCoverage = i++;
GLmethod.scissor = i++;
GLmethod.shaderSource = i++;
GLmethod.stencilFunc = i++;
GLmethod.stencilFuncSeparate = i++;
GLmethod.stencilMask = i++;
GLmethod.stencilMaskSeparate = i++;
GLmethod.stencilOp = i++; //100
GLmethod.stencilOpSeparate = i++;
GLmethod.texImage2D = i++;
GLmethod.texParameterf = i++;
GLmethod.texParameteri = i++;
GLmethod.texSubImage2D = i++;
GLmethod.uniform1f = i++;
GLmethod.uniform1fv = i++;
GLmethod.uniform1i = i++;
GLmethod.uniform1iv = i++;
GLmethod.uniform2f = i++; //110
GLmethod.uniform2fv = i++;
GLmethod.uniform2i = i++;
GLmethod.uniform2iv = i++;
GLmethod.uniform3f = i++;
GLmethod.uniform3fv = i++;
GLmethod.uniform3i = i++;
GLmethod.uniform3iv = i++;
GLmethod.uniform4f = i++;
GLmethod.uniform4fv = i++;
GLmethod.uniform4i = i++; //120
GLmethod.uniform4iv = i++;
GLmethod.uniformMatrix2fv = i++;
GLmethod.uniformMatrix3fv = i++;
GLmethod.uniformMatrix4fv = i++;
GLmethod.useProgram = i++;
GLmethod.validateProgram = i++;
GLmethod.vertexAttrib1f = i++; //new
GLmethod.vertexAttrib2f = i++; //new
GLmethod.vertexAttrib3f = i++; //new
GLmethod.vertexAttrib4f = i++; //new //130
GLmethod.vertexAttrib1fv = i++; //new
GLmethod.vertexAttrib2fv = i++; //new
GLmethod.vertexAttrib3fv = i++; //new
GLmethod.vertexAttrib4fv = i++; //new
GLmethod.vertexAttribPointer = i++;
GLmethod.viewport = i++;
export default GLmethod;

@ -0,0 +1,23 @@
const GLtype = {};
[
"GLbitfield",
"GLboolean",
"GLbyte",
"GLclampf",
"GLenum",
"GLfloat",
"GLint",
"GLintptr",
"GLsizei",
"GLsizeiptr",
"GLshort",
"GLubyte",
"GLuint",
"GLushort"
].sort().map((typeName, i) => GLtype[typeName] = 1 >> (i + 1));
export default GLtype;

@ -0,0 +1,21 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLProgram';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLProgram {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}

@ -0,0 +1,21 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLRenderBuffer';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLRenderbuffer {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}

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

Loading…
Cancel
Save