持续集成分支

This commit is contained in:
aixianling
2024-10-31 14:34:57 +08:00
parent 6a833be062
commit 8c56cf808b
2165 changed files with 4116 additions and 8716 deletions

View File

@@ -0,0 +1,236 @@
<template>
<div class="AppMerchantManage">
<AiTopFixed>
<u-search placeholder="请输入姓名、手机号、店名" :show-action="false" v-model="keyword" confirm-type="search" @clear="current = 1, getList()" @search="current = 1, getList()"/>
</AiTopFixed>
<div class="list">
<u-swipe-action
class="item"
:show="item.show"
:index="index"
v-for="(item, index) in list"
:key="item.id"
@click="onClick"
@open="onOpen"
@content-click="toDetail(item.id, index)"
:options="options">
<div class="title">
<img :src="$cdn + 'xincheng/icon.png'" alt="">
<p>{{ item.businessName }}</p>
<span>店员 ({{ item.userCount }}) </span>
</div>
<div class="info">
<img :src="$cdn + 'xincheng/dh.png'" alt="">
<div>{{ item.bossName }} {{ item.telephone }}</div>
</div>
<div class="info">
<img :src="$cdn + 'xincheng/dz@2x.png'" alt="">
<div>{{ item.businessAddress }}</div>
</div>
<div class="info" style="margin-bottom: 0;">
<img :src="$cdn + 'xincheng/wgz@2x.png'" alt="">
<div v-if="item.girdUserName">{{ item.girdUserName }}-{{ item.girdUserType }}</div>
<div v-else>未绑定网格员</div>
</div>
</u-swipe-action>
<AiEmpty v-if="!list.length"></AiEmpty>
</div>
<div class="btn" @click="linkTo('./add')">新增商户</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'AppMerchantManage',
appName: '商户管理',
data() {
return {
keyword: '',
current: 1,
pages: 2,
list: [],
options: [
{
text: '编辑',
style: {
backgroundColor: '#007aff'
}
},
{
text: '删除',
style: {
backgroundColor: '#dd524d'
}
}
]
}
},
computed: {...mapState(['user'])},
onLoad () {
this.$loading()
this.getList()
uni.$on('update', () => {
this.current = 1
this.getList()
})
},
methods: {
getList () {
if (this.current > this.pages && this.current !== 1) return
this.$http.post('/app/appcompany/getPageWithGird', null, {
params: {
size: 10,
current: this.current,
businessName: this.keyword
}
}).then((res) => {
if (res.code == 0) {
this.list = this.current > 1 ? [...this.list, ...res.data.records.map(v => {
return {
...v,
show: false
}
})] : res.data.records.map(v => {
return {
...v,
show: false
}
})
this.pages = res.data.pages
}
})
},
toDetail (id, index) {
this.list[index].show = false
this.linkTo('./detail?id=' + id)
},
onClick (index, i) {
if (i == 1) {
this.$confirm('确定删除该数据?').then(() => {
this.$http.post('/app/appcompany/delete?id=' + this.list[index].id).then(res => {
if (res.code === 0) {
this.current = 1
this.getList()
}
})
}).catch(() => {
})
} else {
this.list[index].show = false
this.linkTo('./add?id=' + this.list[index].id)
}
},
onOpen (index) {
this.list[index].show = true
this.list.map((val, idx) => {
if(index != idx) {
this.list[idx].show = false
}
})
},
linkTo (url) {
uni.navigateTo({
url
})
}
},
onReachBottom() {
this.current ++
this.getList()
}
}
</script>
<style lang="scss" scoped>
.AppMerchantManage {
.list{
padding: 24px 30px 136px;
.item{
width: 100%;
background: #FFF;
border-radius: 16px;
padding: 32px 24px 32px 32px;
box-sizing: border-box;
margin-bottom: 30px;
&:active {
// background: #eee;
}
.title{
display: flex;
align-items: center;
margin-bottom: 38px;
img{
width: 72px;
height: 72px;
margin-right: 16px;
vertical-align: middle;
}
p{
display: inline-block;
line-height: 1.3;
width: 400px;
font-size: 32px;
font-weight: 500;
color: #000;
}
span{
display: inline-block;
width: calc(100% - 488px);
text-align: right;
font-size: 24px;
font-family: PingFangSC-Regular, PingFang SC;
color: #999;
line-height: 24px;
}
}
.info{
line-height: 1;
margin-bottom: 20px;
img{
width: 28px;
height: 28px;
margin-right: 16px;
}
div{
display: inline-block;
width: calc(100% - 44px);
word-break: break-all;
font-size: 28px;
font-family: PingFangSC-Regular, PingFang SC;
color: #333;
line-height: 40px;
vertical-align: text-top;
}
}
}
}
.btn{
position: fixed;
bottom: 0;
left: 0;
width: 100%;
text-align: center;
height: 112px;
line-height: 112px;
background: #3975C6;
box-shadow: inset 0px 2px 0px 0px #EEEEEE;
font-size: 32px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #FFF;
}
}
</style>

View File

@@ -0,0 +1,338 @@
<template>
<div class="Attendance-address">
<AiTMap
:map.sync="map"
:lib.sync="lib"
:ops="ops"
:libraries="['service', 'tools']">
</AiTMap>
<u-popup v-model="isShow" :closeable="false" border-radius="32" height="70%" mode="bottom">
<div class="wrapper">
<div class="top">
<span @click="isShow = false">取消</span>
<span @click="confirm">确定</span>
</div>
<div class="address-search">
<div class="address-search__wrapper">
<image :src="$cdn + 'xincheng/search.png'" />
<input placeholder-style="color: #98A6B6" placeholder="搜索地点" v-model="address" @input="onChange">
</div>
</div>
<scroll-view scroll-y scroll-into-view="address-item1">
<div class="address-item" :id="'address-item' + index" v-for="(item, index) in addressList" :key="index" @click="chooseAddress(index)">
<div class="left">
<h2>{{ item.title }}</h2>
<p>{{ item._distance >= 0 ? item._distance + 'm内' : '未知' }} | {{ item.address }}</p>
</div>
<div class="right" :class="[currIndex === index ? 'active' : '']"></div>
</div>
</scroll-view>
</div>
</u-popup>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
name: 'ChooseAddess',
appName: '选择地点',
data () {
return {
latitude: '',
longitude: '',
isShow: false,
address: '',
currIndex: 0,
chooseIndex: 0,
map: null,
ops: {},
lib: null,
latLng: null,
addressList: [],
page: 1,
marker: null,
timeout: null
}
},
watch: {
map (v) {
if (v) {
this.init()
}
}
},
onLoad (query) {
},
methods: {
...mapActions(['injectJWeixin']),
init () {
this.map.setZoom(19)
this.addMarker(this.map.getCenter())
this.map.on('click', e => {
this.addressList = []
this.latLng = e.latLng
this.isShow = true
this.addMarker(e.latLng)
this.getAddress()
})
},
confirm () {
const address = this.addressList[this.currIndex]
uni.$emit('chooseAddress', {
address: address.address,
title: address.title,
lat: address.location.lat,
lng: address.location.lng
})
uni.navigateBack({
delta: 1
})
},
chooseAddress (index) {
this.addMarker(this.addressList[index].location)
this.currIndex = index
},
addMarker (position) {
if (this.marker) {
this.marker.setMap(null)
this.marker = null
}
this.marker = new this.lib.MultiMarker({
id: 'marker-layer',
map: this.map,
styles: {
marker: new this.lib.MarkerStyle({
width: 30,
height: 45,
anchor: { x: 10, y: 30 }
}),
},
geometries: [{
id: 'marker',
styleId: 'marker',
position: position,
properties: {
title: 'marker'
}
}]
})
this.easeTo(position)
},
easeTo (position) {
this.map.easeTo({
center: position,
zoom: 17
},
{ duration: 500 })
},
onChange () {
if (this.timeout) {
clearTimeout(this.timeout)
}
this.timeout = setTimeout(() => {
this.currIndex = -1
new this.lib.service.Suggestion({
pageSize: 20
}).getSuggestions({
keyword: this.address
}).then(res => {
this.addressList = []
if (res.data.length) {
this.addressList = res.data
}
})
}, 500)
},
getAddress () {
this.currIndex = 0
new this.lib.service.Search({
pageSize: 20
}).explore({
center: this.latLng,
radius: 2000,
orderBy: '_distance'
}).then(res => {
this.addressList = []
if (res.data.length) {
this.addressList = res.data
}
})
}
}
}
</script>
<style lang="scss" scoped>
.Attendance-address {
width: 100%;
height: 100vh;
.ActionSheet {
background: #F5F6F6;
.ActionSheet-list {
margin-bottom: 8px;
background: #fff;
div {
height: 112px;
line-height: 112px;
text-align: center;
color: #333333;
font-size: 32px;
border-bottom: 1px solid #D8DDE6;
&.active {
color: #1365DD;
font-weight: 600;
}
&:active {
background: #eee;
}
&:last-child {
border: none;
}
}
}
.cancel-btn {
height: 112px;
line-height: 112px;
color: #333;
font-size: 32px;
text-align: center;
font-weight: 600;
background: #fff;
&:active {
background: #eee;
}
}
}
* {
box-sizing: border-box;
line-height: 1;
font-style: normal;
}
.wrapper {
height: 100%;
overflow: hidden;
scroll-view {
height: calc(100% - 308px + 112px);
padding: 0 32px;
.address-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32px 0;
border-bottom: 1px solid #EEEEEE;
&:last-child {
border: none;
}
.left {
flex: 1;
margin-right: 20px;
}
.right {
flex-shrink: 1;
width: 32px;
height: 32px;
border-radius: 50%;
border: 4px solid #CCCCCC;
transition: all ease 0.3s;
&.active {
border: 10px solid #1365DD;
}
}
h2 {
line-height: 1.3;
margin-bottom: 20px;
color: #222222;
font-weight: 600;
font-size: 34px;
}
p {
line-height: 1.2;
color: #999999;
font-size: 28px;
}
}
}
.address-search {
display: flex;
align-items: center;
height: 100px;
padding: 0 32px;
.address-search__wrapper {
display: flex;
align-items: center;
width: 100%;
height: 72px;
padding: 0 32px;
background: #F6F7F9;
border-radius: 36px;
image {
width: 48px;
height: 48px;
}
input {
flex: 1;
font-size: 26px;
color: #333;
}
}
}
.top {
display: flex;
align-items: center;
justify-content: space-between;
height: 96px;
padding: 0 32px;
font-size: 32px;
color: #999;
font-weight: 600;
span:last-child {
color: #222;
}
}
}
map {
width: 100%;
height: 100%;
}
}
</style>

View File

@@ -0,0 +1,246 @@
<template>
<div class="add">
<div class="item">
<span class="tips">*</span>
<div class="border">
<span class="label">店名</span>
<span class="value">
<input type="text" placeholder="请输入" v-model="form.businessName" maxlength="20">
</span>
</div>
</div>
<div class="item mar-b0">
<span class="tips">*</span>
<div class="border">
<span class="label">地址</span>
</div>
</div>
<div class="text-area">
<textarea placeholder="请输入" v-model="form.businessAddress" maxlength="50"></textarea>
</div>
<div class="item">
<span class="tips">*</span>
<div class="border">
<span class="label">店长</span>
<span class="value">
<input type="text" placeholder="请输入" v-model="form.bossName">
</span>
</div>
</div>
<div class="item">
<span class="tips"></span>
<div class="border">
<span class="label">电话</span>
<span class="value">
<input type="text" placeholder="请输入" v-model="form.telephone" maxlength="11">
</span>
</div>
</div>
<div class="item">
<span class="tips"></span>
<div class="border">
<span class="label">身份证号</span>
<span class="value">
<input type="text" placeholder="请输入" v-model="form.cardId" maxlength="18">
</span>
</div>
</div>
<div class="item mar-b0">
<span class="tips"></span>
<div class="border">
<span class="label">门店照片</span>
</div>
</div>
<div class="text-area">
<AiUploader :def.sync="form.storePicUrl" :limit="1" multiple action="/admin/file/add2" style="padding-bottom:16px;"></AiUploader>
</div>
<!-- <AiUploader v-model="form.files" :limit="9" multiple action="/admin/file/add2"></AiUploader> -->
<div class="item">
<span class="tips"></span>
<div class="border">
<span class="label">地图标注</span>
<span class="value" style="color:#3D94FB;" @click="chooseLocation">{{ form.lat ? '已标绘' : '地图标绘' }}</span>
</div>
</div>
<AiGap h="112"/>
<div class="btn" @click="save">保存</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'add',
appName: '新增商户',
data() {
return {
name: '',
files: [],
form: {
bossName: '',
businessAddress: '',
businessName: '',
lat: '',
lng: '',
storePicUrl: [],
telephone: ''
},
id: ''
}
},
computed: {...mapState(['user'])},
onLoad (query) {
if (query.id) {
this.id = query.id
this.getInfo()
}
uni.$on('chooseAddress', e => {
this.form.lat = e.lat
this.form.lng = e.lng
})
},
methods: {
chooseLocation () {
// uni.chooseLocation({
// success: res => {
// this.form.lat = res.latitude
// this.form.lng = res.longitude
// }
// })
uni.navigateTo({
url: './ChooseAddess'
})
},
getInfo () {
this.$loading()
this.$http.post(`/app/appcompany/getDetailById?id=${this.id}`).then(res => {
if (res.code === 0) {
this.form = {
...this.form,
...res.data,
storePicUrl: res.data.storePicUrl ? [{url: res.data.storePicUrl}] : []
}
}
})
},
save () {
if (!this.form.bossName) {
return this.$u.toast('请输入店名')
}
if (!this.form.businessAddress) {
return this.$u.toast('请输入所在地址')
}
if (!this.form.businessName) {
return this.$u.toast('请输入店长名称')
}
this.$loading()
this.$http.post('/api/appcompany/addORUpdate', {
...this.form,
storePicUrl: this.form.storePicUrl.length ? this.form.storePicUrl[0].url : ''
}).then(res => {
if (res.code === 0) {
this.$u.toast('提交成功')
setTimeout(() => {
uni.$emit('update')
uni.navigateBack({
delta: 1
})
}, 500)
}
})
}
}
}
</script>
<style lang="scss" scoped>
.add {
.item{
width: 100%;
padding-left: 14px;
background: #FFF;
box-sizing: border-box;
display: flex;
margin-bottom: 16px;
.tips{
width: 18px;
font-size: 32px;
font-family: PingFangSC-Regular, PingFang SC;
color: #FF4466;
line-height: 44px;
padding: 34px 0 34px 0;
}
.border{
width: calc(100% - 18px);
padding: 34px 32px 34px 0;
line-height: 44px;
line-height: 44px;
font-size: 32px;
font-family: PingFangSC-Regular, PingFang SC;
color: #333;
display: flex;
box-sizing: border-box;
.label{
width: 170px;
}
.value{
width: calc(100% - 170px);
text-align: right;
img{
width: 32px;
height: 32px;
margin-left: 8px;
vertical-align: middle;
}
}
.color-999{
color: #999;
}
}
}
.mar-b0{
margin-bottom: 0;
}
.text-area{
width: 100%;
background-color: #fff;
padding: 0 32px;
margin-bottom: 16px;
box-sizing: border-box;
textarea{
width: 100%;
height: 88px;
padding-bottom: 32px;
}
}
.btn{
position: fixed;
bottom: 0;
left: 0;
width: 100%;
text-align: center;
height: 112px;
line-height: 112px;
background: #3975C6;
box-shadow: inset 0px 2px 0px 0px #EEEEEE;
font-size: 32px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #FFF;
}
}
</style>

View File

@@ -0,0 +1,140 @@
<template>
<div class="addClerk">
<div class="item">
<span class="tips">*</span>
<div class="border">
<span class="label">姓名</span>
<span class="value">
<input type="text" placeholder="请输入" v-model="userName">
</span>
</div>
</div>
<div class="item">
<span class="tips"></span>
<div class="border">
<span class="label">电话</span>
<span class="value">
<input type="tel" placeholder="请输入" v-model="userPhone" maxlength="11">
</span>
</div>
</div>
<div class="btn" @click="save">确认添加</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'addClerk',
appName: '新增店员',
data () {
return {
id: '',
userPhone: '',
userName: ''
}
},
computed: {...mapState(['user'])},
onLoad (query) {
this.id = query.id
},
methods: {
save () {
if (!this.userName) {
return this.$u.toast('请输入姓名')
}
if (!this.userPhone) {
// return this.$u.toast('请输入电话')
}
this.$loading()
this.$http.post('/app/CompanyUser/addOrupdate', {
userName: this.userName,
userPhone: this.userPhone,
companyId: this.id
}).then(res => {
if (res.code === 0) {
this.$u.toast('提交成功')
setTimeout(() => {
uni.$emit('updateUser')
uni.navigateBack({
delta: 1
})
}, 500)
}
})
}
}
}
</script>
<style lang="scss" scoped>
.addClerk {
.item{
width: 100%;
padding-left: 14px;
background: #FFF;
box-sizing: border-box;
display: flex;
margin-bottom: 16px;
.tips{
width: 18px;
font-size: 32px;
font-family: PingFangSC-Regular, PingFang SC;
color: #FF4466;
line-height: 44px;
padding: 34px 0 34px 0;
}
.border{
width: calc(100% - 18px);
padding: 34px 32px 34px 0;
line-height: 44px;
line-height: 44px;
font-size: 32px;
font-family: PingFangSC-Regular, PingFang SC;
color: #333;
display: flex;
box-sizing: border-box;
.label{
width: 170px;
}
.value{
width: calc(100% - 170px);
text-align: right;
img{
width: 32px;
height: 32px;
margin-left: 8px;
vertical-align: middle;
}
}
.color-999{
color: #999;
}
}
}
.btn{
position: fixed;
bottom: 0;
left: 0;
width: 100%;
text-align: center;
height: 112px;
line-height: 112px;
background: #3975C6;
box-shadow: inset 0px 2px 0px 0px #EEEEEE;
font-size: 32px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #FFF;
}
}
</style>

View File

@@ -0,0 +1,219 @@
<template>
<div class="detail" v-if="pageShow">
<div class="header">
<img :src="$cdn + 'xincheng/icon.png'" alt="" class="logo-img">
<div class="text-info">
<p>{{ info.businessName }}</p>
<div>
<img :src="$cdn + 'xincheng/dw.png'" alt="" class="local-icon">
<span>{{ info.businessAddress }}</span>
</div>
</div>
</div>
<div class="content">
<div class="title">基础信息</div>
<div class="info">
<span>店主</span>
<span class="color-333">{{ info.bossName }}</span>
</div>
<div class="info">
<span>电话</span>
<span class="color-3D94FB" @click="call(info.telephone)">{{ info.telephone }}</span>
</div>
<div class="info">
<span>身份证</span>
<span class="color-333">{{ info.cardId }}</span>
</div>
<div class="img-list">
<div class="info">
<span>门店照片</span>
</div>
<image v-if="info.storePicUrl" :src="info.storePicUrl" mode="aspectFill" @click="preview(info.storePicUrl)" />
</div>
<div class="info">
<span>地图</span>
<span class="color-3D94FB" @click="openLocation">{{ info.lng ? '已定位' : '未定位' }}</span>
</div>
</div>
<div class="content" v-if="info.detailList.length">
<div class="title">店员信息</div>
<div class="info" v-for="(item, index) in info.detailList" :key="index">
<span>{{ item.userName }}</span>
<span class="color-3D94FB" @click="call(item.userPhone)">{{ item.userPhone }}</span>
</div>
</div>
<AiGap h="112"/>
<div class="btn" @click="linkTo('./addClerk?id=' + id)">新增店员</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'detail',
appName: '商户详情',
data () {
return {
info: {},
pageShow: false
}
},
computed: {...mapState(['user'])},
onLoad (query) {
this.id = query.id
this.getInfo()
uni.$on('updateUser', () => {
this.getInfo()
})
},
methods: {
getInfo () {
this.$loading()
this.$http.post(`/app/appcompany/getDetailById?id=${this.id}`).then(res => {
if (res.code === 0) {
this.info = res.data
this.pageShow = true
}
})
},
linkTo (url) {
uni.navigateTo({
url
})
},
call (phone) {
uni.makePhoneCall({
phoneNumber: phone
})
},
openLocation () {
if (!this.info.lat) {
return false
}
uni.openLocation({
latitude: this.info.lat,
longitude: this.info.lng
})
},
preview (url) {
uni.previewImage({
urls: [url],
current: url
})
}
}
}
</script>
<style lang="scss" scoped>
.detail {
.header{
width: 100%;
background-color: #fff;
padding: 48px 32px;
box-sizing: border-box;
margin-bottom: 8px;
.logo-img{
width: 96px;
height: 96px;
margin-right: 24px;
vertical-align: top;
}
.text-info{
display: inline-block;
width: calc(100% - 120px);
p{
font-size: 32px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333;
line-height: 44px;
margin-bottom: 6px;
word-break: break-all;
}
.local-icon{
width: 20px;
height: 26px;
margin-right: 6px;
}
span{
display: inline-block;
width: calc(100% - 32px);
font-size: 28px;
font-family: PingFangSC-Regular, PingFang SC;
color: #90969E;
line-height: 40px;
vertical-align: text-top;
}
}
}
.content{
background-color: #fff;
padding: 0 32px 20px;
margin-bottom: 8px;
.title{
font-size: 32px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: #333;
line-height: 44px;
padding: 32px 0;
}
.info{
display: flex;
justify-content: space-between;
font-size: 32px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999;
line-height: 44px;
padding: 14px 0;
.color-333{
color: #333;
}
.color-3D94FB{
color: #3D94FB;
&:active {
opacity: 0.6;
}
}
}
.img-list{
image {
width: 100%;
height: 320px;
}
}
}
.btn{
position: fixed;
bottom: 0;
left: 0;
width: 100%;
text-align: center;
height: 112px;
line-height: 112px;
background: #3975C6;
box-shadow: inset 0px 2px 0px 0px #EEEEEE;
font-size: 32px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #FFF;
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B