瀏覽代碼

Merge branch 'master' of http://git.zthymaoyi.com/wangchao/businessCard

zhangyuewww 2 年之前
父節點
當前提交
0ee7736102

+ 1 - 1
.gitignore

@@ -76,4 +76,4 @@ unimall.log.*.tmp
 *.wxss
 *.json
 *.wxml
-xiaochengxu/
+xiaochengxu/unpackage/

+ 397 - 0
xiaochengxu/components/lb-picker/README.md

@@ -0,0 +1,397 @@
+# uni-app picker 选择器
+
+插件市场里面的 picker 选择器不满足自己的需求,所以自己写了一个简单的 picker 选择器,可扩展、可自定义,一般满足日常需要。  
+Github:[https://github.com/liub1934/uni-lb-picker](https://github.com/liub1934/uni-lb-picker)  
+插件市场:[https://ext.dcloud.net.cn/plugin?id=1111](https://ext.dcloud.net.cn/plugin?id=1111)
+
+> **由于之前`cancel`拼写失误,写成了`cancle`,`v1.08`现已修正,如果之前版本有使用`cancel`事件的,更新后请及时修正。**
+
+## 兼容性
+
+H5 + 各平台小程序(百度除外)  
+安卓及 IOS 未测试(应该也能运行)
+已知问题:支付宝小程序单选可能会出现不对齐的现象
+
+## 功能
+
+1、单选  
+2、多级联动,非多级联动,理论支持任意级数  
+3、省市区选择,基于多级联动  
+4、自定义选择器头部确定取消按钮颜色及插槽支持  
+5、选择器可视区自定义滚动个数  
+6、自定义数据字段,满足不同人的需求  
+7、自定义选择器样式  
+8、单选及非联动选择支持扁平化的简单数据,如下形式:
+
+```javascript
+// 单选列表
+list1: ['选项1', '选项2', '选项2'],
+// 非联动选择列表
+list2: [
+  ['选项1', '选项2', '选项3'],
+  ['选项11', '选项22', '选项33'],
+  ['选项111', '选项222', '选项333']
+]
+```
+
+## 引入插件
+
+单独引入,在需要使用的页面上 import 引入即可
+
+```html
+<template>
+  <view>
+    <lb-picker></lb-picker>
+  </view>
+</template>
+
+<script>
+  import LbPicker from '@/components/lb-picker'
+  export default {
+    components: {
+      LbPicker
+    }
+  }
+</script>
+```
+
+全局引入,`main.js`中 import 引入并注册即可全局使用
+
+```jsvascript
+import LbPicker from '@/components/lb-picker'
+Vue.component("lb-picker", LbPicker)
+```
+
+npm 安装引入:
+
+```shell
+npm install uni-lb-picker
+```
+
+```jsvascript
+import LbPicker from 'uni-lb-picker'
+```
+
+## 选择器数据格式
+
+### 单选
+
+常规数据
+
+```javascript
+list: [
+  {
+    label: '选项1',
+    value: '1'
+  },
+  {
+    label: '选项2',
+    value: '2'
+  }
+]
+```
+
+扁平化简单数据
+
+```javascript
+list: ['选项1', '选项2']
+```
+
+### 多级联动
+
+```javascript
+list: [
+  {
+    label: '选项1',
+    value: '1',
+    children: [
+      {
+        label: '选项1-1',
+        value: '1-1',
+        children: [
+          {
+            label: '选项1-1-1',
+            value: '1-1-1'
+          }
+        ]
+      }
+    ]
+  }
+]
+```
+
+### 非联动选择
+
+常规数据
+
+```javascript
+list: [
+  [
+    { label: '选项1', value: '1' },
+    { label: '选项2', value: '2' },
+    { label: '选项3', value: '3' }
+  ],
+  [
+    { label: '选项11', value: '11' },
+    { label: '选项22', value: '22' },
+    { label: '选项33', value: '33' }
+  ],
+  [
+    { label: '选项111', value: '111' },
+    { label: '选项222', value: '222' },
+    { label: '选项333', value: '333' }
+  ]
+]
+```
+
+扁平化简单数据
+
+```javascript
+list: [
+  ['选项1', '选项2', '选项3'],
+  ['选项11', '选项22', '选项33'],
+  ['选项111', '选项222', '选项333']
+]
+```
+
+## 调用显示选择器
+
+通过`ref`形式手动调用`show`方法显示,隐藏同理调用`hide`
+
+```text
+<lb-picker ref="picker"></lb-picker>
+
+this.$refs.picker.show() // 显示
+this.$refs.picker.hide() // 隐藏
+```
+
+## 绑定值及设置默认值
+
+支持 vue 中`v-model`写法绑定值,无需自己维护选中值的索引。
+
+```javascript
+<lb-picker v-model="value1"></lb-picker>
+<lb-picker v-model="value2"></lb-picker>
+
+data () {
+  return {
+    value1: '' // 单选
+    value2: [] // 多列联动选择
+  }
+}
+```
+
+## 多个选择器
+
+通过设置不同的`ref`,然后调用即可
+
+```javascript
+<lb-picker ref="picker1"></lb-picker>
+<lb-picker ref="picker2"></lb-picker>
+
+this.$refs.picker1.show() // picker1显示
+this.$refs.picker2.show() // picker2显示
+```
+
+## 省市区选择
+
+省市区选择是基于多列联动选择,数据来源:[https://github.com/modood/Administrative-divisions-of-China](https://github.com/modood/Administrative-divisions-of-China),  
+省市区文件位于`/pages/demos/area-data-min.js`,自行引入即可,可参考`demo3省市区选择`,  
+也可使用自己已有的省市区数据,如果数据字段不一样,也可以自定义,参考下方自定义数据字段。
+
+## 自定义数据字段
+
+为了满足不同人的需求,插件支持自定义数据字段名称, 插件默认的数据字段如下形式:
+
+```javascript
+list: [
+  {
+    label: '选择1',
+    value: 1,
+    children: []
+  },
+  {
+    label: '选择1',
+    value: 1,
+    children: []
+  }
+]
+```
+
+如果你的数据字段和上面不一样,如下形式:
+
+```javascript
+list: [
+  {
+    text: '选择1',
+    id: 1,
+    child: []
+  },
+  {
+    text: '选择1',
+    id: 1,
+    child: []
+  }
+]
+```
+
+通过设置参数中的`props`即可,如下所示:
+
+```javascript
+<lb-picker :props="myProps"></lb-picker>
+
+data () {
+  return {
+    myProps: {
+      label: 'text',
+      value: 'id',
+      children: 'child'
+    }
+  }
+}
+```
+
+## 插槽使用
+
+选择器支持一些可自定义化的插槽,如选择器的取消和确定文字按钮,如果需要对其自定义处理的话,比如加个 icon 图标之类的,可使用插槽,使用方法如下:
+
+```html
+<lb-picker>
+  <view slot="cancel-text">我是自定义取消</view>
+  <view slot="confirm-text">我是自定义确定</view>
+</lb-picker>
+```
+
+其他插槽见下。
+
+## 参数及事件
+
+### Props
+
+| 参数                    | 说明                                                                                        | 类型                | 可选值                                                           | 默认值                                            |
+| :---------------------- | :------------------------------------------------------------------------------------------ | :------------------ | :--------------------------------------------------------------- | :------------------------------------------------ |
+| value/v-model           | 绑定值,联动选择为 Array 类型                                                               | String/Number/Array | -                                                                | -                                                 |
+| mode                    | 选择器类型,支持单列,多列联动                                                              | String              | selector 单选/multiSelector 多级联动/unlinkedSelector 多级非联动 | selector                                          |
+| list                    | 选择器数据(v1.0.7 单选及非联动多选支持扁平数据:['选项 1', '选项 2'])                       | Array               | -                                                                | -                                                 |
+| level                   | 多列联动层级,仅 mode 为 multiSelector 有效                                                 | Number              | -                                                                | 2                                                 |
+| props                   | 自定义数据字段                                                                              | Object              | -                                                                | {label:'label',value:'value',children:'children'} |
+| cancel-text             | 取消文字                                                                                    | String              | -                                                                | 取消                                              |
+| cancel-color            | 取消文字颜色                                                                                | String              | -                                                                | #999                                              |
+| confirm-text            | 确定文字                                                                                    | String              | -                                                                | 确定                                              |
+| confirm-color           | 确定文字颜色                                                                                | String              | -                                                                | #007aff                                           |
+| empty-text              | (v1.0.7 新增)选择器列表为空的时候显示的文字                                                 | String              | -                                                                | 暂无数据                                          |
+| empty-color             | (v1.0.7 新增)暂无数据文字颜色                                                               | String              | -                                                                | #999                                              |
+| column-num              | 可视滚动区域内滚动个数,最好设置奇数值                                                      | Number              | -                                                                | 5                                                 |
+| radius                  | 选择器顶部圆角,支持 rpx,如 radius="10rpx"                                                 | String              | -                                                                | -                                                 |
+| ~~column-style~~        | ~~选择器默认样式(已弃用,见下方自定义样式说明)~~                                            | Object              | -                                                                | -                                                 |
+| ~~active-column-style~~ | ~~选择器选中样式(已弃用,见下方自定义样式说明)~~                                            | Object              | -                                                                | -                                                 |
+| loading                 | 选择器是否显示加载中,可使用 loading 插槽自定义加载效果                                     | Boolean             | -                                                                | -                                                 |
+| mask-color              | 遮罩层颜色                                                                                  | String              | -                                                                | rgba(0, 0, 0, 0.4)                                |
+| close-on-click-mask     | 点击遮罩层是否关闭选择器                                                                    | Boolean             | true/false                                                       | true                                              |
+| ~~change-on-init~~      | ~~(v1.0.7 已弃用)初始化时是否触发 change 事件~~                                             | Boolean             | true/false                                                       | -                                                 |
+| dataset                 | (v1.0.7 新增)可以向组件中传递任意的自定义的数据,在`confirm`或`change`事件中可以取到        | Object              | -                                                                | -                                                 |
+| show-header             | (v1.0.8 新增)是否显示选择器头部                                                             | Boolean             | -                                                                | - true                                            |
+| inline                  | (v1.0.8 新增)inline 模式,开启后默认显示选择器,无需点击弹出,可以配合`show-header`一起使用 | Boolean             | -                                                                | -                                                 |
+
+### 方法
+
+| 方法名 | 说明       | 参数 |
+| :----- | :--------- | :--- |
+| show   | 打开选择器 | -    |
+| hide   | 关闭选择器 | -    |
+
+### Events
+
+| 事件名称 | 说明                                     | 回调参数                                                                                                                                                                                                                                                                                                                             |
+| :------- | :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| show     | 选择器打开时触发                         | -                                                                                                                                                                                                                                                                                                                                    |
+| hide     | 选择器隐藏时触发                         | -                                                                                                                                                                                                                                                                                                                                    |
+| change   | 选择器滚动时触发,此时不会改变绑定的值   | `{ index, item, value, change }` `index`触发滚动后新的索引,单选时是具体的索引值,多列联动选择时为数组。`item`触发滚动后新的的完整内容,包括`label`、`value`等,单选时为对象,多列选择时为数组对象。`value`触发滚动后新的 value 值,单列选择时为具体值,多列联动选择时为数组。`change`触发事件的类型,详情参考下面的 change 事件备注 |
+| confirm  | 点击选择器确定时触发,此时会改变绑定的值 | 同上`change`事件说明                                                                                                                                                                                                                                                                                                                 |
+| cancel   | 点击选择器取消时触发                     | 同上`change`事件说明                                                                                                                                                                                                                                                                                                                 |
+
+### `change` 事件备注
+
+如果绑定的值是空的,`change`触发后里面的内容都是列表的第一项。  
+`change`事件会在以下情况触发:
+
+- 初始化
+- 绑定值 value 变化
+- 选择器 list 列表变化
+- 滚动选择器
+
+以上情况会在回调函数中都可以取到`change`变化的类型,对应上面的情况包括以下:
+
+- `init`
+- `value`
+- `list`
+- `scroll`
+
+根据这些类型大家可以在`change`的时候按需处理自己的业务逻辑,比如一种常见的情况,有默认值的时候需要显示默认值的文字,此时可以`change`事件中判断`change`的类型是否是`init`,如果是的话可以取事件回调中的`item`进行显示绑定值对应的文字信息。
+
+```javascript
+handleChange (e) {
+  if (e.change === 'init') {
+    console.log(e.item.label) // 单选 选项1
+    console.log(e.item.map(item => item.label).join('-')) // 多选 选项1-选项11
+  }
+}
+```
+
+### 插槽
+
+| 插槽名        | 说明                |
+| :------------ | :------------------ |
+| cancel-text   | 选择器取消文字插槽  |
+| action-center | 选择器顶部中间插槽  |
+| confirm-text  | 选择器确定文字插槽  |
+| loading       | 选择器 loading 插槽 |
+| empty         | 选择器 空数据 插槽  |
+
+### 选择器自定义样式
+
+原先的`column-style`和`active-column-style`已弃用,如需修改默认样式及选中样式参考`demo9`
+
+```css
+<style lang="scss" scoped>
+/deep/ .lb-picker {
+  .lb-picker-column-label {
+    color: #f0ad4e;
+  }
+  .lb-picker-column-active {
+    .lb-picker-column-label {
+      color: #007aff;
+      font-weight: 700;
+    }
+  }
+}
+</style>
+```
+
+### 获取选中值的文字
+
+`@confirm`事件中可以拿到:
+
+单选:
+
+```javascript
+handleConfirm (e) {
+  console.log(e.item.label) // 选项1
+}
+```
+
+联动选择:
+
+```javascript
+handleConfirm (e) {
+  console.log(e.item.map(item => item.label).join('-')) // 选项1-选项11
+}
+```
+
+## Tips
+
+微信小程序端,滚动时在 iOS 自带振动反馈,可在系统设置 -> 声音与触感 -> 系统触感反馈中关闭
+
+## 其他
+
+其他功能参考示例 Demo 代码。

+ 256 - 0
xiaochengxu/components/lb-picker/index.vue

@@ -0,0 +1,256 @@
+<template>
+  <view :class="['lb-picker', inline ? 'lb-picker-inline' : '']">
+    <view class="lb-picker-mask"
+      v-show="visible && !inline"
+      :style="{ 'background-color': maskColor }"
+      @tap.stop="handleMaskTap"
+      @touchmove.stop.prevent="moveHandle">
+    </view>
+    <view :class="['lb-picker-container', visible ? 'lb-picker-toggle' : '']"
+      :style="{ borderRadius: `${radius} ${radius} 0 0` }">
+      <view v-if="showHeader"
+        class="lb-picker-header"
+        :style="{
+          height: pickerHeaderHeight,
+          'line-height': pickerHeaderHeight
+        }">
+        <view class="lb-picker-action lb-picker-left">
+          <view class="lb-picker-action-cancel"
+            @tap.stop="handleCancel">
+            <slot v-if="$slots['cancel-text']"
+              name="cancel-text"> </slot>
+            <view v-else
+              class="action-cancel-text"
+              :style="{ color: cancelColor }" style="font-size: 28rpx;">
+              {{ cancelText }}
+            </view>
+          </view>
+        </view>
+
+        <view class="lb-picker-action lb-picker-center"
+          v-if="$slots['action-center']">
+          <slot name="action-center"></slot>
+        </view>
+
+        <view class="lb-picker-action lb-picker-right">
+          <view class="lb-picker-action-confirm"
+            @tap.stop="handleConfirm">
+            <slot v-if="$slots['confirm-text']"
+              name="confirm-text"> </slot>
+            <view v-else
+              class="action-confirm-text"
+               style="font-size: 28rpx;color: #FA6400;">
+              {{ confirmText }}
+            </view>
+          </view>
+        </view>
+      </view>
+      <view class="lb-picker-content"
+        :style="{ height: pickerContentHeight }">
+        <!-- loading -->
+        <view v-if="loading"
+          class="lb-picker-loading">
+          <slot name="loading">
+            <view class="lb-picker-loading-img"></view>
+          </slot>
+        </view>
+
+        <!-- 暂无数据 -->
+        <view v-if="isEmpty && !loading"
+          class="lb-picker-empty">
+          <slot name="empty">
+            <text class="lb-picker-empty-text"
+              :style="{ color: emptyColor }">
+              {{ emptyText }}
+            </text>
+          </slot>
+        </view>
+
+        <!-- 单选 -->
+        <selector-picker v-if="mode === 'selector' && !loading && !isEmpty"
+          :value="value"
+          :list="list"
+          :props="pickerProps"
+          :height="pickerContentHeight"
+          :inline="inline"
+          @change="handleChange">
+        </selector-picker>
+
+        <!-- 多列联动 -->
+        <multi-selector-picker v-if="mode === 'multiSelector' && !loading && !isEmpty"
+          :value="value"
+          :list="list"
+          :level="level"
+          :visible="visible"
+          :props="pickerProps"
+          :height="pickerContentHeight"
+          :inline="inline"
+          @change="handleChange">
+        </multi-selector-picker>
+
+        <!-- 非联动选择 -->
+        <unlinked-selector-picker v-if="mode === 'unlinkedSelector' && !loading && !isEmpty"
+          :value="value"
+          :list="list"
+          :visible="visible"
+          :props="pickerProps"
+          :height="pickerContentHeight"
+          :inline="inline"
+          @change="handleChange">
+        </unlinked-selector-picker>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+const defaultProps = {
+  label: 'label',
+  value: 'value',
+  children: 'children'
+}
+import { getIndicatorHeight } from './utils'
+import SelectorPicker from './pickers/selector-picker'
+import MultiSelectorPicker from './pickers/multi-selector-picker'
+import UnlinkedSelectorPicker from './pickers/unlinked-selector-picker'
+const indicatorHeight = getIndicatorHeight()
+export default {
+  components: {
+    SelectorPicker,
+    MultiSelectorPicker,
+    UnlinkedSelectorPicker
+  },
+  props: {
+    value: [String, Number, Array],
+    list: Array,
+    mode: {
+      type: String,
+      default: 'selector'
+    },
+    level: {
+      type: Number,
+      default: 1
+    },
+    props: {
+      type: Object
+    },
+    cancelText: {
+      type: String,
+      default: '取消'
+    },
+    cancelColor: String,
+    confirmText: {
+      type: String,
+      default: '确定'
+    },
+    confirmColor: '#FA6400',
+    canHide: {
+      type: Boolean,
+      default: true
+    },
+    emptyColor: String,
+    emptyText: {
+      type: String,
+      default: '暂无数据'
+    },
+    radius: String,
+    columnNum: {
+      type: Number,
+      default: 5
+    },
+    loading: Boolean,
+    closeOnClickMask: {
+      type: Boolean,
+      default: true
+    },
+    maskColor: {
+      type: String,
+      default: 'rgba(0, 0, 0, 0.4)'
+    },
+    dataset: Object,
+    inline: Boolean,
+    showHeader: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data () {
+    return {
+      visible: false,
+      myValue: this.value,
+      picker: {},
+      pickerProps: Object.assign({}, defaultProps, this.props),
+      pickerHeaderHeight: indicatorHeight + 'px',
+      pickerContentHeight: indicatorHeight * this.columnNum + 'px'
+    }
+  },
+  computed: {
+    isEmpty () {
+      if (!this.list) return true
+      if (this.list && !this.list.length) return true
+      return false
+    }
+  },
+  methods: {
+    show () {
+      if (this.inline) return
+      this.visible = true
+    },
+    hide () {
+      if (this.inline) return
+      this.visible = false
+    },
+    handleCancel () {
+      this.$emit('cancel', this.picker)
+      if (this.canHide && !this.inline) {
+        this.hide()
+      }
+    },
+    handleConfirm () {
+      if (this.isEmpty) {
+        this.$emit('confirm', null)
+        this.hide()
+      } else {
+        const picker = JSON.parse(JSON.stringify(this.picker))
+        this.myValue = picker.value
+        this.$emit('confirm', this.picker)
+        if (this.canHide) this.hide()
+      }
+    },
+    handleChange ({ value, item, index, change }) {
+      this.picker.value = value
+      this.picker.item = item
+      this.picker.index = index
+      this.picker.change = change
+      this.picker.dataset = this.dataset || {}
+      this.$emit('change', this.picker)
+    },
+    handleMaskTap () {
+      if (this.closeOnClickMask) {
+        this.visible = false
+        this.$emit('visibleCityPicker', false)
+      }
+    },
+    moveHandle () {}
+  },
+  watch: {
+    value (newVal) {
+      this.myValue = newVal
+    },
+    myValue (newVal) {
+      this.$emit('input', newVal)
+    },
+    visible (newVisible) {
+      if (newVisible) {
+        this.$emit('show')
+      } else {
+        this.$emit('hide')
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "./style/picker.scss";
+</style>

+ 131 - 0
xiaochengxu/components/lb-picker/pickers/multi-selector-picker.vue

@@ -0,0 +1,131 @@
+<template>
+  <view class="multi-selector picker-item"
+    :style="{ height: height }">
+    <picker-view :value="pickerValue"
+      :indicator-style="indicatorStyle"
+      :style="{ height: height }"
+      @change="handleChange">
+      <picker-view-column v-for="(column, index) in pickerColumns"
+        :key="index">
+        <view v-for="(item, i) in column || []"
+          :class="[
+            'lb-picker-column',
+            item[props.value] === selectValue[index]
+              ? 'lb-picker-column-active'
+              : ''
+          ]"
+          :key="i"
+          :style="{ height: columnHeight, 'line-height': columnHeight }">
+          <view class="lb-picker-column-label">
+            {{ item[props.label] }}
+          </view>
+        </view>
+      </picker-view-column>
+    </picker-view>
+  </view>
+</template>
+
+<script>
+import { getIndicatorHeight } from '../utils.js'
+const indicatorHeight = getIndicatorHeight()
+export default {
+  props: {
+    value: Array,
+    list: Array,
+    props: Object,
+    level: Number,
+    visible: Boolean,
+    height: String
+  },
+  data () {
+    return {
+      pickerValue: [],
+      pickerColumns: [],
+      selectValue: [],
+      selectItem: [],
+      columnHeight: indicatorHeight + 'px',
+      indicatorStyle: `height: ${indicatorHeight}px`
+    }
+  },
+  created () {
+    this.init('init')
+  },
+  methods: {
+    init (changeType) {
+      this.setPickerItems(this.list)
+      this.$emit('change', {
+        value: this.selectValue,
+        item: this.selectItem,
+        index: this.pickerValue,
+        change: changeType
+      })
+    },
+    handleChange (item) {
+      const pickerValue = item.detail.value
+      const columnIndex = pickerValue.findIndex(
+        (item, i) => item !== this.pickerValue[i]
+      )
+      const valueIndex = pickerValue[columnIndex]
+      this.setPickerChange(pickerValue, valueIndex, columnIndex)
+    },
+    setPickerChange (pickerValue, valueIndex, columnIndex) {
+      for (let i = 0; i < this.level; i++) {
+        if (i > columnIndex) {
+          pickerValue[i] = 0
+          const column =
+            this.pickerColumns[i - 1][valueIndex] ||
+            this.pickerColumns[i - 1][0]
+          this.$set(this.pickerColumns, i, column[this.props.children] || [])
+          valueIndex = 0
+        }
+        this.pickerValue = pickerValue
+        this.selectItem[i] = this.pickerColumns[i][pickerValue[i]]
+        if (this.selectItem[i]) {
+          this.selectValue[i] = this.selectItem[i][this.props.value]
+        } else {
+          const spliceNum = this.level - i
+          this.pickerValue.splice(i, spliceNum)
+          this.selectValue.splice(i, spliceNum)
+          this.selectItem.splice(i, spliceNum)
+          this.pickerColumns.splice(i, spliceNum)
+          break
+        }
+      }
+      this.$emit('change', {
+        value: this.selectValue,
+        item: this.selectItem,
+        index: this.pickerValue,
+        change: 'scroll'
+      })
+    },
+    setPickerItems (list = [], index = 0) {
+      if (!list.length) return
+      const defaultValue = this.value || []
+      if (index < this.level) {
+        const value = defaultValue[index] || ''
+        let i = list.findIndex(item => item[this.props.value] === value)
+        i = i > -1 ? i : 0
+        this.$set(this.pickerValue, index, i)
+        this.$set(this.pickerColumns, index, list)
+        if (list[i]) {
+          this.$set(this.selectValue, index, list[i][this.props.value])
+          this.$set(this.selectItem, index, list[i])
+          this.setPickerItems(list[i][this.props.children] || [], index + 1)
+        }
+      }
+    }
+  },
+  watch: {
+    value (newVal) {
+      this.init('value')
+    },
+    list () {
+      this.init('list')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "../style/picker-item.scss";
+</style>

+ 99 - 0
xiaochengxu/components/lb-picker/pickers/selector-picker.vue

@@ -0,0 +1,99 @@
+<template>
+  <view class="selector-picker picker-item"
+    :style="{ height: height }">
+    <picker-view :value="pickerValue"
+      :indicator-style="indicatorStyle"
+      :style="{ height: height }"
+      @change="handleChange">
+      <picker-view-column>
+        <view v-for="(item, i) in list"
+          :class="[
+            'lb-picker-column',
+            item[props.value] || item === selectValue
+              ? 'lb-picker-column-active'
+              : ''
+          ]"
+          :key="i"
+          :style="{ height: columnHeight, lineHeight: columnHeight }">
+          <view class="lb-picker-column-label">
+            {{ item[props.label] || item }}
+          </view>
+        </view>
+      </picker-view-column>
+    </picker-view>
+  </view>
+</template>
+
+<script>
+import { getIndicatorHeight, isObject } from '../utils.js'
+const indicatorHeight = getIndicatorHeight()
+export default {
+  props: {
+    value: [String, Number],
+    list: Array,
+    props: Object,
+    visible: Boolean,
+    height: String
+  },
+  data () {
+    return {
+      pickerValue: [],
+      selectValue: '',
+      columnHeight: indicatorHeight + 'px',
+      indicatorStyle: `height: ${indicatorHeight}px`
+    }
+  },
+  created () {
+    this.init('init')
+  },
+  methods: {
+    init (changeType) {
+      if (this.list && this.list.length) {
+        let index = this.list.findIndex(item => {
+          return isObject(item)
+            ? item[this.props.value] === this.value
+            : item === this.value
+        })
+        index = index > -1 ? index : 0
+        const listItem = this.list[index]
+        this.pickerValue = [index]
+        this.selectValue = isObject(listItem)
+          ? listItem[this.props.value]
+          : listItem
+        this.$emit('change', {
+          value: this.selectValue,
+          item: listItem,
+          index: index,
+          change: changeType
+        })
+      }
+    },
+    handleChange (item) {
+      const index = item.detail.value[0] || 0
+      const listItem = this.list[index]
+      this.selectValue = isObject(listItem)
+        ? listItem[this.props.value]
+        : listItem
+      this.pickerValue = item.detail.value
+      this.$emit('change', {
+        value: this.selectValue,
+        item: listItem,
+        index: index,
+        change: 'scroll'
+      })
+    }
+  },
+  watch: {
+    list () {
+      this.init('list')
+    },
+    value (newVal) {
+      this.init('value')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "../style/picker-item.scss";
+</style>

+ 120 - 0
xiaochengxu/components/lb-picker/pickers/unlinked-selector-picker.vue

@@ -0,0 +1,120 @@
+<template>
+  <view class="selector-picker picker-item"
+    :style="{ height: height }">
+    <picker-view :value="pickerValue"
+      :indicator-style="indicatorStyle"
+      :style="{ height: height }"
+      @change="handleChange">
+      <picker-view-column v-for="(column, index) in pickerColumns"
+        :key="index">
+        <view v-for="(item, i) in column"
+          :class="[
+            'lb-picker-column',
+            item[props.value] || item === selectValue[index]
+              ? 'lb-picker-column-active'
+              : ''
+          ]"
+          :key="i"
+          :style="{ height: columnHeight, 'line-height': columnHeight }">
+          <view class="lb-picker-column-label">
+            {{ item[props.label] || item }}
+          </view>
+        </view>
+      </picker-view-column>
+    </picker-view>
+  </view>
+</template>
+
+<script>
+import { getIndicatorHeight, isObject } from '../utils.js'
+const indicatorHeight = getIndicatorHeight()
+export default {
+  props: {
+    value: Array,
+    list: Array,
+    props: Object,
+    visible: Boolean,
+    height: String,
+    indicatorHeight: Number
+  },
+  data () {
+    return {
+      pickerValue: [],
+      pickerColumns: [],
+      selectValue: [],
+      selectItem: [],
+      columnHeight: indicatorHeight + 'px',
+      indicatorStyle: `height: ${indicatorHeight}px`
+    }
+  },
+  created () {
+    this.init('init')
+  },
+  methods: {
+    init (changeType) {
+      if (this.list && this.list.length) {
+        this.pickerColumns = this.list
+        this.setPickerValue()
+        this.$emit('change', {
+          value: this.selectValue,
+          item: this.selectItem,
+          index: this.pickerValue,
+          change: changeType
+        })
+      }
+    },
+    setPickerValue (value) {
+      this.list.forEach((item, i) => {
+        let index = item.findIndex(item => {
+          return isObject(item)
+            ? item[this.props.value] === this.value[i]
+            : item === this.value[i]
+        })
+        index = index > -1 ? index : 0
+        const columnItem = this.list[i][index]
+        const valueItem = isObject(columnItem)
+          ? columnItem[this.props.value]
+          : columnItem
+        this.$set(this.pickerValue, i, index)
+        this.$set(this.selectValue, i, valueItem)
+        this.$set(this.selectItem, i, columnItem)
+      })
+    },
+
+    handleChange (item) {
+      const pickerValue = item.detail.value
+      const columnIndex = pickerValue.findIndex(
+        (item, i) => item !== this.pickerValue[i]
+      )
+      if (columnIndex > -1) {
+        const valueIndex = pickerValue[columnIndex]
+        const columnItem = this.list[columnIndex][valueIndex]
+        const valueItem = isObject(columnItem)
+          ? columnItem[this.props.value]
+          : columnItem
+        this.pickerValue = pickerValue
+        this.$set(this.selectValue, columnIndex, valueItem)
+        this.$set(this.selectItem, columnIndex, columnItem)
+        this.$emit('change', {
+          value: this.selectValue,
+          item: this.selectItem,
+          index: this.pickerValue,
+          change: 'scroll'
+        })
+      }
+    }
+  },
+  watch: {
+    list () {
+      this.init('list')
+    },
+    value (newVal) {
+      this.init('value')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "../style/picker-item.scss";
+</style>

+ 27 - 0
xiaochengxu/components/lb-picker/style/picker-item.scss

@@ -0,0 +1,27 @@
+.picker-item {
+	picker-view-column {
+		transition: all 0.3s;
+	}
+
+	.lb-picker-column-flex0 {
+		flex: 0;
+	}
+
+	.lb-picker-column-flex1 {
+		flex: 1;
+	}
+
+	.lb-picker-column {
+		padding: 0;
+		font-size: 36rpx;
+		text-align: center;
+		box-sizing: border-box;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+		overflow: hidden;
+
+		.lb-picker-column-label {
+			transition: all 0.3s;
+		}
+	}
+}

文件差異過大導致無法顯示
+ 94 - 0
xiaochengxu/components/lb-picker/style/picker.scss


+ 55 - 52
xiaochengxu/pages/circle/circle.vue

@@ -1,9 +1,6 @@
 <template>
 	<view class="content">
 		<view class="content1 flex">
-			<!-- <uni-search-bar @confirm="search" v-model="searchValue" @blur="blur" @focus="focus" @input="input"
-				@cancel="cancel" @clear="clear" cancelButton="false" placeholder="搜索圈子">
-			</uni-search-bar> -->
 			<view class="search flex flex-between" @click="search">
 				<view class="left flex">
 					<uni-icons type="search" size="24"></uni-icons>
@@ -18,37 +15,37 @@
 				<u-badge :isDot="true" type="error" class="point"></u-badge>
 			</view>
 		</view>
-		<view class="content2 flex flex-between">
-			<view class="left">
-				我得圈子
-			</view>
-			<view class="right">
-				<uni-icons type="personadd-filled" size="30" @click="toCreateCircle"></uni-icons>
-			</view>
-		</view>
-		<!-- <mescroll-body ref="mescrollRef" @init="mescrollInit" @up="upCallback" @down="downCallback">
-					<view class="content3" v-for="(item,index) in circleList">
-			
-					</view>
-				</mescroll-body> -->
-		<view class="content3 flex">
-			<view class="row-tiem" @click="toDetail">
+		<mescroll-body ref="mescrollRef" @init="mescrollInit" @up="upCallback" @down="downCallback">
+			<view class="content2 flex flex-between">
 				<view class="left">
-					<image src="../../static/logo.png" mode="widthFix" class="img"></image>
+					我得圈子
 				</view>
 				<view class="right">
-					<view class="top">北京技术(155)</view>
-					<view class="bottom">
-						<span>粮食贸易</span>
-						<span>粮食贸易</span>
-						<span>粮食贸易</span>
-						<span>粮食贸易</span>
+					<uni-icons type="personadd-filled" size="30" @click="toCreateCircle"></uni-icons>
+				</view>
+			</view>
+			<view class="" v-for="(item,index) in circleList" :key="index">
+				<view class="" v-if="item.name">
+					{{item.name}}
+				</view>
+				<view class="content3 flex" v-if="!item.name">
+					<view class="row-tiem" @click="toDetail">
+						<view class="left">
+							<image src="../../static/logo.png" mode="widthFix" class="img"></image>
+						</view>
+						<view class="right">
+							<view class="top">{{item.circleName}}({{item.cardNum}})</view>
+							<view class="bottom">
+								<span v-for="item1 in item.circleLabel.split(',')">
+									{{item1}}
+								</span>
+							</view>
+						</view>
 					</view>
 				</view>
 			</view>
-
-		</view>
-	</view>
+		</mescroll-body>
+		<u-toast ref="uToast"></u-toast>
 	</view>
 </template>
 
@@ -65,30 +62,23 @@
 		},
 		data() {
 			return {
-					searchVal: '',
-				searchValue: '',
+				searchVal: '',
 				circleList: [],
 				userInfo: {},
+				firstTitle: false
 			};
 		},
 		onShow() {
 			this.searchVal = uni.getStorageSync("cirlce_search_val") ? uni.getStorageSync("cirlce_search_val") : ''
-			// this.$nextTick(function() {
-			// 	this.canReset && this.mescroll.resetUpScroll() // 重置列表数据为第一页  
-			// 	this.canReset && this.mescroll.scrollTo(0, 0) // 重置列表数据为第一页时,建议把滚动条也重置到顶部,避免无法再次翻页的问题  
-			// 	this.canReset = true // 过滤第一次的onShow事件,避免初始化界面时重复触发upCallback, 无需配置auto:false
-			// });
-			// if (this.hasLogin || uni.getStorageSync("userInfo").username) {
-			// 	this.userInfo = uni.getStorageSync("userInfo")
-			// 	this.isLogin = true
-			// } else {
-			// 	uni.navigateTo({
-			// 		url: "/pages/login/login"
-			// 	})
-			// }
+			this.userInfo = uni.getStorageSync("userInfo")
+			this.$nextTick(function() {
+				this.canReset && this.mescroll.resetUpScroll() // 重置列表数据为第一页  
+				this.canReset && this.mescroll.scrollTo(0, 0) // 重置列表数据为第一页时,建议把滚动条也重置到顶部,避免无法再次翻页的问题  
+				this.canReset = true // 过滤第一次的onShow事件,避免初始化界面时重复触发upCallback, 无需配置auto:false
+			});
 		},
 		methods: {
-			toCreateCircle(){
+			toCreateCircle() {
 				uni.navigateTo({
 					url: "/pages/circle/createCirclce"
 				})
@@ -110,10 +100,8 @@
 				uni.showLoading({
 					title: '数据加载中'
 				})
-				this.$request.baseRequest('pincheCarSharingApp', 'list', {
-					remark2: this.route,
-					carpoolingType: this.type,
-					companyId: 1,
+				this.$request.baseRequest('admin.unimall.circleManagementInfo', 'list', {
+					commonId: this.userInfo.id,
 					pageNum: page.num,
 					pageSize: page.size,
 				}, failres => {
@@ -124,7 +112,6 @@
 					})
 					uni.hideLoading()
 				}).then(res => {
-					// if (res.errno == 200) {
 					uni.hideLoading()
 					console.log(11)
 					let curPageData = res.data.items;
@@ -133,9 +120,25 @@
 					this.mescroll.endByPage(curPageLen, totalPage);
 					console.log(res.data)
 					// this.makeData(res.data)
-					if (page.num == 1) this.infoList = []; //如果是第一页需手动置空列表
-					this.infoList = this.infoList.concat(curPageData); //追加新数据
-					// }
+					if (page.num == 1) this.circleList = []; //如果是第一页需手动置空列表
+					this.circleList = this.circleList.concat(curPageData); //追加新数据
+					for (let i = 0; i < this.circleList.length; i++) {
+						if (!this.firstTitle && this.circleList[i].addedFlag == 0) {
+							this.firstTitle = true
+							if (i == 0) {
+								this.circleList.unshift({
+									name: '推荐圈子'
+								});
+							} else {
+								this.circleList.splice(i - 1, 0, {
+									name: '推荐圈子'
+								})
+							}
+							
+							console.log(this.circleList)
+							return
+						}
+					}
 				})
 			},
 			input(res) {

+ 100 - 10
xiaochengxu/pages/circle/createCirclce.vue

@@ -4,7 +4,7 @@
 		<uni-forms :modelValue="formData">
 			<view class="content1">
 				<uni-forms-item label="圈子名称" name="name">
-					<uni-easyinput type="text" v-model="formData.name" placeholder="输入圈子名称" />
+					<uni-easyinput type="text" v-model="formData.circleName" placeholder="输入圈子名称" />
 				</uni-forms-item>
 			</view>
 			<view class="content2">
@@ -12,29 +12,119 @@
 					圈子标签
 				</view>
 				<view class="">
-					<uni-forms-item label="标签1" name="name">
+					<uni-forms-item :label="'标签'+(index+1)" name="labelName" v-for="(item,index) in labelList"
+						:key="index">
 						<view class="flex">
-							<uni-easyinput type="text" v-model="formData.name" placeholder="输入圈子标签,2-4个字" />
-							<uni-icons type="plus-filled"></uni-icons>
-							<uni-icons type="minus-filled"></uni-icons>
+							<uni-easyinput type="text" v-model="item.labelName" placeholder="输入圈子标签,2-4个字" />
+							<uni-icons type="plus-filled" @click="add(item)" size="40"></uni-icons>
+							<uni-icons type="minus-filled" @click="subtract(index)" size="40"></uni-icons>
 						</view>
-						
 					</uni-forms-item>
 				</view>
 			</view>
-			<view class="">
-				<button type="primary">提交</button>
-			</view>
 		</uni-forms>
+
+		<view class="">
+			<button type="primary" @click="submit">提交</button>
+		</view>
+		<u-toast ref="uToast"></u-toast>
 	</view>
 </template>
 
 <script>
+	var that;
 	export default {
 		data() {
 			return {
-				formData: {}
+				labelList: [{
+						labelName: ''
+					},
+					{
+						labelName: ''
+					}
+				],
+				formData: {
+					circleName: '',
+					circleLabel: [],
+					commonId: ''
+
+				}
 			};
+		},
+		onLoad() {
+			that = this
+			this.userInfo = uni.getStorageSync("userInfo")
+			this.formData.commonId = this.userInfo.id
+		},
+		methods: {
+			submit() {
+				this.formData.circleLabel = []
+				if (!this.formData.circleName) {
+					this.$refs.uToast.show({
+						type: 'error',
+						message: '圈子名称不能为空!',
+					})
+					return
+				}
+				for (let i = 0; i < this.labelList.length; i++) {
+					if (!this.labelList[i].labelName) {
+						this.$refs.uToast.show({
+							type: 'error',
+							message: '标签内容不能为空!',
+						})
+						return
+					}
+					if (this.labelList[i].labelName.length > 4 || this.labelList[i].labelName.length < 2) {
+						this.$refs.uToast.show({
+							type: 'error',
+							message: '标签内容2-4个字!',
+						})
+						return
+					}
+					this.formData.circleLabel.push(this.labelList[i].labelName)
+				}
+				this.formData.circleLabel = this.formData.circleLabel.toString()
+
+				this.$request.baseRequest('admin.unimall.circleManagementInfo', 'add', {
+					circleManagementInfo: JSON.stringify(this.formData)
+				}, failres => {
+					this.$refs.uToast.show({
+						type: 'error',
+						message: failres.errmsg,
+					})
+					uni.hideLoading()
+				}).then(res => {
+					this.$refs.uToast.show({
+						type: 'success',
+						message: '提交成功!',
+					})
+					setTimeout(() => {
+						uni.navigateBack()
+					}, 500)
+				})
+			},
+			add() {
+				if (this.labelList.length > 3) {
+					this.$refs.uToast.show({
+						type: 'error',
+						message: '“标签数量2-4个!',
+					})
+					return
+				}
+				this.labelList.push({
+					labelName: ''
+				})
+			},
+			subtract(index) {
+				if (this.labelList.length < 3) {
+					this.$refs.uToast.show({
+						type: 'error',
+						message: '“标签数量2-4个!',
+					})
+					return
+				}
+				this.labelList.splice(index, 1)
+			},
 		}
 	}
 </script>

+ 5 - 0
xiaochengxu/pages/index/index.vue

@@ -9,6 +9,7 @@
 
 			</view>
 		</mescroll-body> -->
+		<u-toast ref="uToast"></u-toast>
 	</view>
 </template>
 
@@ -32,6 +33,10 @@
 			}
 		},
 		onShow() {
+			// this.$refs.uToast.show({
+			// 	type: 'success',
+			// 	message: '提交成功!',
+			// })
 			// this.$nextTick(function() {
 			// 	this.canReset && this.mescroll.resetUpScroll() // 重置列表数据为第一页  
 			// 	this.canReset && this.mescroll.scrollTo(0, 0) // 重置列表数据为第一页时,建议把滚动条也重置到顶部,避免无法再次翻页的问题  

+ 169 - 0
xiaochengxu/pages/mySet/editCard.vue

@@ -0,0 +1,169 @@
+<template>
+	<view class="content">
+		<view class="content1">
+			<view class="left">
+				<image :src="cardInfo.headSculpture" mode="widthFix" class="img"></image>
+			</view>
+			<view class="right">
+				<view class="row1">
+					{{cardInfo.name}}|{{cardInfo.post}}
+				</view>
+				<view class="">
+					{{cardInfo.companyName}}
+				</view>
+				<view class="">
+					地址
+				</view>
+				<view class="">
+					联系电话
+				</view>
+				<view class="">
+					备注
+				</view>
+			</view>
+		</view>
+		<view class="change-template">
+			切换模板
+		</view>
+		<view class="content2">
+			<view class="">
+				名片信息
+			</view>
+			<u--form labelPosition="left" :model="cardInfo" ref="uForm">
+				<u-form-item label="名片标识" prop="cardInfo.cardBusiness" borderBottom>
+					<u--input v-model="cardInfo.cardBusiness" border="none" placeholder="输入名片标识"></u--input>
+				</u-form-item>
+				<u-form-item label="姓名" prop="cardInfo.name" borderBottom>
+					<u--input v-model="cardInfo.name" border="none" placeholder="输入姓名"></u--input>
+				</u-form-item>
+				<u-form-item label="职务" prop="cardInfo.post" borderBottom>
+					<u--input v-model="cardInfo.post" border="none" placeholder="输入职务,不超过8个字符"></u--input>
+				</u-form-item>
+				<u-form-item label="公司/机构名称" prop="cardInfo.companyName" borderBottom>
+					<u--input v-model="cardInfo.companyName" border="none" placholder="输入公司/机构名称"></u--input>
+				</u-form-item>
+				</u-form-item>
+				<!-- 		<u-form-item label="地址" prop="userInfo.name" borderBottom>
+					<view @click="showPicker">选择地址</view>
+					<view>{{ province }}{{ city }}{{ area }}</view>
+				</u-form-item> -->
+				<u-form-item label="定位" prop="cardInfo.name" borderBottom @click="placeSelect();hideKeyboard()">
+					<text>选择地址> </text>
+				</u-form-item>
+				<u-form-item label="详细地址" prop="cardInfo.name" borderBottom>
+					<u--input v-model="cardInfo.detailedAddress" border="none" placeholder="输入地址,不超过15个字"></u--input>
+				</u-form-item>
+				<u-form-item label="联系电话" prop="cardInfo.name" borderBottom>
+					<u--input v-model="cardInfo.phone" border="none" placeholder="输入联系电话"></u--input>
+				</u-form-item>
+			</u--form>
+		</view>
+		<view class="content3">
+			<view class="left">
+				上传企业logo或个人头像
+			</view>
+			<view class="right">
+				<u-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" multiple
+					:maxCount="1"></u-upload>
+			</view>
+		</view>
+		<view class="bottom">
+			<button @click="submit">提交</button>
+		</view>
+		<city-picker ref="picker" mode="multiSelector" :list="areaData" :level="3" @confirm="finishSelectAddress">
+		</city-picker>
+		<u-modal :show="isSubmit" :content='content' @confirm="$u.debounce(confirmSubmit, 500)" showCancelButton
+			@cancel="isSubmit=false" @close="isSubmit=false" closeOnClickOverlay></u-modal>
+		<u-toast ref="uToast"></u-toast>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				isSubmit: false,
+				content: '确定修改名片信息?',
+				cardInfo:{},
+				fileList1: [],
+			};
+		},
+		onLoad(options) {
+			this.cardInfo = JSON.parse(options.itemVal)
+			console.log(this.cardInfo)
+			this.fileList1 = [
+				{
+					url:this.cardInfo.headSculpture
+				}
+			]
+			
+		},
+		methods:{
+			confirmSubmit() {
+				console.log(123434343)
+				console.log(123434343)
+				this.$request.baseRequest('admin.unimall.cardManagementInfo', 'update', {
+					cardManagementInfo: JSON.stringify(this.cardInfo)
+				}, failres => {
+					this.$refs.uToast.show({
+						type: 'error',
+						message: failres.errmsg,
+					})
+					uni.hideLoading()
+				}).then(res => {
+					this.isSubmit = false
+					this.$refs.uToast.show({
+						type: 'success',
+						message: '修改成功!',
+					})
+				})
+			},
+			submit() {
+				this.isSubmit = true
+			
+			},
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			// 新增图片
+			async afterRead(event) {
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+			},
+			uploadFilePromise(res) {
+				return new Promise((resolve, reject) => {
+					uploadImage(res, 'cardImages/',
+						result => {
+							that.cardInfo.headSculpture = result
+							resolve(res)
+						}
+					)
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.img {
+		width: 100rpx;
+	}
+</style>

部分文件因文件數量過多而無法顯示