# 二开示例:快递公司管理

注意:该功能已有,这里只是以该功能介绍下如何二开 vk-mall-admin

# 功能列表

  1. 查看快递公司列表
  2. 添加快递公司
  3. 修改快递公司
  4. 删除快递公司

其他要求

  1. 菜单加在订单管理下面

# 云对象

# 开始写云对象

由于vkmall采用admin端绑定client端的方式,故云对象要在client端写。

注意:vkmall采用云对象模式来写云函数。传送门 - 云对象文档 (opens new window)

在vkmall的client端的目录uniCloud/cloudfunctions/router/service/admin/business/ 写云对象 sys.expressCo

该云对象需要写以下接口:

  1. getList(查看快递公司列表)
  2. addUpdate(添加或修改快递公司)
  3. delete(删除快递公司)
  4. getMaxSort(获取当前最大的sort值)

这里云对象用 sys. 开头代表此云对象默认需要授权才可以访问。

sys.expressCo 云对象代码如下

'use strict';
var vk; // 全局vk实例
// 涉及的表名
const dbName = require("../../../dao/config.js");

var db = uniCloud.database(); // 全局数据库引用
var _ = db.command; // 数据库操作符
var $ = _.aggregate; // 聚合查询操作符
/**
 * 权限注意:访问以下链接查看
 * 文档地址:https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#内置权限
 */
var cloudObject = {
	isCloudObject: true, // 标记为云对象模式
	/**
	 * 请求前处理,主要用于调用方法之前进行预处理,一般用于拦截器、统一的身份验证、参数校验、定义全局对象等。
	 * 文档地址:https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#before-预处理
	 */
	_before: async function() {
		vk = this.vk; // 将vk定义为全局对象
		// let { customUtil, uniID, config, pubFun } = this.getUtil(); // 获取工具包
	},
	/**
	 * 请求后处理,主要用于处理本次调用方法的返回结果或者抛出的错误
	 * 文档地址:https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#after-后处理
	 */
	_after: async function(options) {
		let { err, res } = options;
		if (err) {
			return; // 如果方法抛出错误,直接return;不处理
		}
		return res;
	},
	/**
	 * 获取列表(此处用kh_开头,代表登录即可访问,无需授权)
	 * @url admin/business/sys.expressCo.kh_getList 前端调用的url参数地址
	 */
	kh_getList: async function(data) {
		let res = { code: 0, msg: '' };
		// 业务逻辑开始-----------------------------------------------------------
		res = await vk.daoCenter.expressCoDao.getTableData({
			data,
			sortArr: [{ "name": "sort", "type": "desc" }],
		});
		// 业务逻辑结束-----------------------------------------------------------
		return res;
	},
	/**
	 * 添加和修改
	 * @url admin/business/sys.expressCo.addUpdate 前端调用的url参数地址
	 */
	addUpdate: async function(data) {
		let res = { code: 0, msg: '' };
		// 业务逻辑开始-----------------------------------------------------------
		let {
			_id,
			sort = 0,
			logo,
			name,
			com,
			phone,
			url,
			remark
		} = data;

		let dataJson = {
			sort,
			logo,
			name,
			com,
			phone,
			url,
			remark
		};
		if (vk.pubfn.isNull(_id)) {
			// 添加
			_id = await vk.daoCenter.expressCoDao.add(dataJson);
			res.msg = "添加成功";
		} else {
			// 修改并返回修改后的数据
			res.info = await vk.daoCenter.expressCoDao.updateAndReturn({
				whereJson: {
					_id
				},
				dataJson
			});
			res.num = res.info ? 1 : 0;
			res.msg = "修改成功";
		}
		// 业务逻辑结束-----------------------------------------------------------
		return res;
	},
	/**
	 * 删除
	 * @url admin/business/sys.expressCo.delete 前端调用的url参数地址
	 */
	delete: async function(data) {
		let res = { code: 0, msg: '' };
		// 业务逻辑开始-----------------------------------------------------------
		let {
			_id
		} = data;
		await vk.daoCenter.expressCoDao.deleteById(_id);
		// 业务逻辑结束-----------------------------------------------------------
		return res;
	},
	/**
	 * 当前最大的sort
	 * @url admin/business/sys.expressCo.getMaxSort 前端调用的url参数地址
	 */
	getMaxSort: async function(data) {
		let res = { code: 0, msg: '' };
		// 业务逻辑开始-----------------------------------------------------------
		let sort = await vk.daoCenter.expressCoDao.max({
			fieldName: "sort"
		});
		res.sort = sort || 0;
		// 业务逻辑结束-----------------------------------------------------------
		return res;
	},
	/**
	 * 模板函数
	 * @url admin/business/sys.expressCo.test 前端调用的url参数地址
	 */
	test: async function(data) {
		let res = { code: 0, msg: '' };
		// 业务逻辑开始-----------------------------------------------------------


		// 业务逻辑结束-----------------------------------------------------------
		return res;
	},
};

module.exports = cloudObject;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  • 写完后上传云函数 router

# 云对象代码简单介绍

const dbName = require("../../../dao/config.js"); 是引用了公共的数据表名配置文件,通过 dbName.expressCo 来获取快递公司管理的数据库表名 vk-mall-express-co

vk.daoCenter.expressCoDao 是快递公司表的数据库操作API,关于Dao的作用见 传送门 - dao层的作用 (opens new window)

addUpdate函数 同时具备了添加和修改的能力(因为一般添加和修改大部分逻辑是一样的,故可写成一个函数,通过是否传_id来判断添加还是修改)

该云对象的功能为:

  1. getList(查看快递公司列表)
  2. addUpdate(添加或修改快递公司)
  3. delete(删除快递公司)
  4. getMaxSort(获取当前最大的sort值)

# 页面

# 开始写页面

页面写在 vk-mall-admin 项目的 pages 目录下

页面完整代码如下

<template>
	<view class="page-body">
		<!-- 页面内容开始 -->

		<!-- 表格搜索组件开始 -->
		<vk-data-table-query
			v-model="queryForm1.formData"
			:columns="queryForm1.columns"
			size="mini"
			@search="search"
		></vk-data-table-query>
		<!-- 表格搜索组件结束 -->

		<!-- 自定义按钮区域开始 -->
		<view>
			<el-row>
				<el-button type="success" size="small" icon="el-icon-circle-plus-outline" @click="addBtn">添加</el-button>
			</el-row>
		</view>
		<!-- 自定义按钮区域结束 -->

		<!-- 表格组件开始 -->
		<vk-data-table
			ref="table1" 
			:action="table1.action"
			:columns="table1.columns"
			:query-form-param="queryForm1"
			:right-btns="['detail_auto','update','delete']"
			:selection="true"
			:row-no="true"
			:pagination="true"
			size="mini"
			@update="updateBtn"
			@delete="deleteBtn"
			@current-change="currentChange"
			@selection-change="selectionChange"
		>
			<!-- 排序值 -->
			<template v-slot:sort="{ row, column, index }">
				<el-input v-model="row.sort" size="mini" @change="sortChange($event, row)"/>
			</template>
		</vk-data-table>
		<!-- 表格组件结束 -->

		<!-- 添加或编辑的弹窗开始 -->
		<vk-data-dialog
			v-model="form1.props.show"
			:title="form1.props.title"
			width="500px"
			mode="form"
			:close-on-click-modal="true"
		>
			<vk-data-form
				v-model="form1.data"
				:rules="form1.props.rules"
				:action="form1.props.action"
				:form-type="form1.props.formType"
				:columns='form1.props.columns'
				label-width="140px"
				@success="form1.props.show = false;refresh();"
			></vk-data-form>
		</vk-data-dialog>
		<!-- 添加或编辑的弹窗结束 -->

		<!-- 页面内容结束 -->
	</view>
</template>

<script>
	var that;													// 当前页面对象
	var vk = uni.vk;									// vk实例
	var originalForms = {};						// 表单初始化数据

	export default {
		data() {
			// 页面数据变量
			return {
				// 页面是否请求中或加载中
				loading:false,
				// init请求返回的数据
				data:{

				},
				// 表格相关开始 -----------------------------------------------------------
				table1:{
					// 表格数据请求地址
					action:"admin/business/sys.expressCo.kh_getList",
					// 表格字段显示规则
					columns:[
						{ key:"sort", title:"排序", type:"text", width:80, sortable:"custom" },
						{ key:"logo", title:"logo", type:"image", width:80 },
						{ key:"name", title:"名称", type:"text", width:100 },
						{ key:"com", title:"字母简称", type:"text", width:100 },
						{ key:"phone", title:"客服电话", type:"text", width:80 },
						{ key:"url", title:"官网", type:"text", width:200, align:"left" },
						{ key:"remark", title:"备注", type:"text", minWidth:120, align:"left" },
					],
					// 多选框选中的值
					multipleSelection:[],
					// 当前高亮的记录
					selectItem:""
				},
				// 表格相关结束 -----------------------------------------------------------
				// 表单相关开始 -----------------------------------------------------------
				// 查询表单请求数据
				queryForm1:{
					// 查询表单数据源,可在此设置默认值
					formData:{

					},
					// 查询表单的字段规则 fieldName:指定数据库字段名,不填默认等于key
					columns:[
						{ key:"name", title:"公司名称", type:"text", mode:"%%" },
						{ key:"com", title:"字母简称", type:"text", mode:"%%" },
					]
				}, 
				form1:{
					// 表单请求数据,此处可以设置默认值
					data: {

					},
					// 表单属性
					props: {
						// 表单请求地址
						action:"",
						// 表单字段显示规则
						columns:[
							{ key:"sort", title:"排序", type:"text" },
							{ key:"logo", title:"快递公司logo", type:"image", limit:1 },
							{ key:"name", title:"快递公司名称", type:"text" },
							{ key:"com", title:"快递公司字母简称", type:"text" },
							{ key:"phone", title:"快递公司客服电话", type:"text" },
							{ key:"url", title:"快递公司官网", type:"text" },
							{ key:"remark", title:"备注", type:"text" },
						],
						// 表单验证规则
						rules:{
							sort: [
								{ required: true, message: "该项必填", trigger: ['blur','change'] }
							],
							name: [
								{ required: true, message: "该项必填", trigger: ['blur','change'] }
							]
						},
						// add 代表添加 update 代表修改
						formType:"",
						// 是否显示表单的弹窗
						show:false
					}
				},
				// 其他弹窗表单
				formDatas:{},
				// 表单相关结束 -----------------------------------------------------------
			};
		},
		// 监听 - 页面每次【加载时】执行(如:前进)
		onLoad(options = {}) {
			that = this;
			vk = that.vk;
			that.options = options;
			that.init(options);
		},
		// 监听 - 页面【首次渲染完成时】执行。注意如果渲染速度快,会在页面进入动画完成前触发
		onReady() {},
		// 监听 - 页面每次【显示时】执行(如:前进和返回) (页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面)
		onShow() {},
		// 监听 - 页面每次【隐藏时】执行(如:返回)
		onHide() {},
		// 函数
		methods: {
			// 页面数据初始化函数
			init(options) {
				originalForms["form1"] = vk.pubfn.copyObject(that.form1);
			},
			// 页面跳转
			pageTo(path) {
				vk.navigateTo(path);
			},
			// 表单重置
			resetForm(){
				vk.pubfn.resetForm(originalForms, that);
			},
			// 搜索
			search(){
				that.$refs.table1.search();
			},
			// 刷新
			refresh(){
				that.$refs.table1.refresh();
			},
			// 获取当前选中的行的数据
			getCurrentRow(){
				return that.$refs.table1.getCurrentRow();
			},
			// 监听 - 行的选中高亮事件
			currentChange(val){
				that.table1.selectItem = val;
			},
			// 当选择项发生变化时会触发该事件
			selectionChange(list) {
				that.table1.multipleSelection = list;
			},
			// 显示添加页面
			addBtn(){
				that.resetForm();
				that.form1.props.action = 'admin/business/sys.expressCo.addUpdate';
				that.form1.props.formType = 'add';
				that.form1.props.title = '添加';
				that.form1.props.show = true;
				// 获取当前最大sort的值,+1
				vk.callFunction({
					url: 'admin/business/sys.expressCo.getMaxSort',
					data: {

					},
					success: (data) => {
						that.$set(that.form1.data,"sort",data.sort+1);
					}
				});
			},
			// 显示修改页面
			updateBtn({ item }){
				that.form1.props.action = 'admin/business/sys.expressCo.addUpdate';
				that.form1.props.formType = 'update';
				that.form1.props.title = '编辑';
				that.form1.props.show = true;
				that.form1.data = item;
			},
			// 删除按钮
			deleteBtn({ item, deleteFn }){
				deleteFn({
					action:"admin/business/sys.expressCo.delete",
					data:{
						_id: item._id
					},
				});
			},
			sortChange(sort, item){
				vk.callFunction({
					url: 'admin/business/sys.expressCo.addUpdate',
					data: {
						_id: item._id,
						sort: Number(sort)
					},
					success: (data) => {

					}
				});
			},
		},
		// 监听属性
		watch: {

		},
		// 计算属性
		computed: {

		}
	};
</script>
<style lang="scss" scoped>
	.page-body {

	}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  • 3、在 pages.jsonPages中配置页面路由
"pages": [
  ...其他页面
  { "path": "pages/order/express/list", "style": { "navigationBarTitleText": "快递公司" } },
],
1
2
3
4
  • 4、在菜单管理新增菜单
  1. 菜单名称填:快递公司
  2. URL:/pages/order/express/list
  3. 父级菜单:选择订单管理
  • 5、完成

# 页面代码简单介绍

template 主要写了这几个组件

  1. vk-data-table-query(实现万能表格顶部的查询功能)传送门 - 万能表格搜索组件文档 (opens new window)
  2. vk-data-table(实现万能表格功能)传送门 - 万能表格文档 (opens new window)
  3. vk-data-form(实现万能表单功能)传送门 - 万能表单文档 (opens new window)

script 的data内有以下几个重要的变量

  1. table1(万能表格的配置)
  2. queryForm1(万能表格的查询配置)
  3. form1(万能表单的配置)

methods内则是一些基础操作的函数。

# 总结

  1. 先在 client端 写云对象。
  2. 复制 admin端 下的 pages_template/kong/list.vue,在此文件的基础上修改 scriptdata 内的 table1 queryForm1 form1 的配置,并修改 methods 内的 addBtnupdateBtndeleteBtn 的代码即可。

# 二开vk-mall-admin需要掌握的知识点

# 云端

传送门 - 云对象文档 (opens new window)

传送门 - 数据库操作 API (opens new window)

传送门 - 数据库操作 常见问题 (opens new window)

# 页面

传送门 - 万能表格文档 (opens new window)

传送门 - 万能表格搜索组件文档 (opens new window)

传送门 - 万能表单文档 (opens new window)