Просмотр исходного кода

Merge branch 'master' of http://110.41.163.243:9980/kd-aiot/aiot-app

ysy 1 год назад
Родитель
Сommit
6a104390fc

+ 0 - 1
main.js

@@ -6,7 +6,6 @@ Vue.prototype.$isAuthorities = (authorities)=>{
 	try {
 		let authoritiesLiat=uni.getStorageSync('authorities') //按钮
 		let authoritiesCode=JSON.parse(authoritiesLiat).map(item=>item.permissionCode)
-		console.log(authoritiesCode)
 		return authoritiesCode.includes(authorities)
 	} catch (error) {
 		//TODO handle the exception

+ 17 - 0
pages.json

@@ -2036,7 +2036,24 @@
 				"navigationStyle": "custom",
 				"navigationBarTextStyle": "white"
 			}
+		},
+		{
+			"path": "pages/doc/selectDoc",
+			"style": {
+				"navigationBarTitleText": "关联文档库",
+				"navigationStyle": "custom",
+				"navigationBarTextStyle": "white"
+			}
+		},
+		{
+			"path": "pages/doc/docList",
+			"style": {
+				"navigationBarTitleText": "文档管理",
+				"navigationStyle": "custom",
+				"navigationBarTextStyle": "white"
+			}
 		}
+		
 	],
 	"tabBar": {
 		"color": "#908f8f",

+ 111 - 0
pages/doc/api/index.js

@@ -0,0 +1,111 @@
+import {
+	postJ,
+	post,
+	get
+} from "@/utils/request";
+import Vue from "vue";
+
+
+
+/**
+ * 查询文件夹分类数据
+ * @data data
+ */
+export async function getDocTreeListAPI(data) {
+  const res = await postJ(Vue.prototype.apiUrl+'/fm/directory/selectTreeList', data);
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+
+/**
+* 查询文件数据
+* @data data
+*/
+export async function filePageAPI(data) {
+  const res = await postJ(Vue.prototype.apiUrl+'/fm/file/pageBusiness', data);
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+/**
+ * 保存
+ * @data data
+ */
+export async function fileSaveAPI(data) {
+  const res = await postJ(Vue.prototype.apiUrl+'/fm/file/save', data);
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+
+/**
+ * 详情
+ * @id id
+ */
+export async function fileGetByIdAPI(id) {
+  const res = await request.get(`/fm/file/getById/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+/**
+ * ids查询文件列表
+ * @data data
+ */
+ export async function queryIds(data) {
+  const res = await postJ(Vue.prototype.apiUrl+'/fm/file/queryIds', data);
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+
+
+
+
+// 获取编码树
+export async function selectTreeList(data) {
+  const res = await postJ(Vue.prototype.apiUrl+`/main/business_code_category/selectTreeList`, data);
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+// 获取右侧列表
+export async function listParentId(data) {
+  const res = await postJ(Vue.prototype.apiUrl+`/main/business_code_category/listPageParentId`, data);
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+
+/**
+ * 上传文件
+ * @param file 文件
+ */
+ export async function uploadFileNew (data) {
+  const formData = new FormData();
+  formData.append('multiPartFile', data.multiPartFile);
+  formData.append('module', data.module);
+  const res = await post(Vue.prototype.apiUrl+'/main/file/uploadFile', formData);
+  if (res.code === '0') {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+// 编码方案
+export async function listCode() {
+  const res = await get(Vue.prototype.apiUrl+`/main/business_code_category/listCode/WD0001`);
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+

+ 53 - 0
pages/doc/browse.vue

@@ -0,0 +1,53 @@
+<template>
+<!--   <web-view
+      :src="fileUrl"
+      width="100%"
+      v-if="showEditFlag"
+      style="height: calc(100vh - 100px)"
+      frameborder="0"
+      allowfullscreen="true"
+    ></web-view> -->
+</template>
+
+<script>
+import { getFileType } from './util.js';
+
+export default {
+  data() {
+    return {
+      fileUrl: '',
+      showEditFlag: false
+    };
+  },
+
+  methods: {
+    open(row){
+      this.showEditFlag=true
+      this.setFileUrl(row)
+    },
+    setFileUrl(row) {
+      let file = row.storagePath[0];
+      let fileNames=file.storePath.split('/')
+      let url = window.location.origin+'/api/main/file/getFile?objectName='+file.storePath+'&fullfilename='+fileNames[fileNames.length-1]
+      this.fileUrl = '/kkfile/onlinePreview?url=' + btoa(url);
+      
+      // const fileExt = file.name.substr(file.name.lastIndexOf('.') + 1);
+      // this.fileType = getFileType().includes(fileExt) ? 2 : 1;
+      // if (this.fileType == 2) {
+      //   this.fileUrl =
+      //     '/fm/' +
+      //     (['dxf', 'dwg'].includes(fileExt) ? '2DViewer' : '3DViewer') +
+      //     '/view.html?url=' +
+      //     sessionStorage.filePath +
+      //     '/' +
+      //     row.outputNdsFiles;
+      // } else {
+      //   this.fileUrl = '/kkfile/onlinePreview?url=' + btoa(url);
+      //   // 'http://192.168.1.107:18082/kkfile/onlinePreview?url=' + btoa(url);
+      // }
+    }
+  }
+};
+</script>
+<style scoped lang="scss">
+</style>

+ 203 - 0
pages/doc/docList.vue

@@ -0,0 +1,203 @@
+<template>
+	<view class="mainBox">
+		<uni-nav-bar fixed="true" statusBar="true" left-icon="back" title="文档列表" @clickLeft="back">
+
+		</uni-nav-bar>
+		<view class="topBtn">
+			<u-button type="primary" @click="add" text="本地上传"></u-button>
+			<u-button @click="selectDoc" text="关联文档库"></u-button>
+		</view>
+
+		<u-cell-group>
+			<u-cell v-for="(item, index) in tableList" :key="index">
+				<view class="listTitle" slot="title">
+					{{item.name}}
+				</view>
+
+				<view slot="value" class="listBtn">
+
+					<u-button text="浏览" size='mini'></u-button>
+					<u-button text="删除" size='mini' @click="del(index)"></u-button>
+				</view>
+			</u-cell>
+		</u-cell-group>
+
+		<view style="height: 84rpx;"></view>
+		<view class="footerButton">
+			<u-button type="primary" @click="back" text="返回"></u-button>
+			<u-button @click="save" text="保存"></u-button>
+		</view>
+		<fileEdit  ref="fileEditRef" @success="done"></fileEdit>
+	</view>
+</template>
+
+<script>
+	import {
+		queryIds
+	} from './api';
+	import fileEdit from './file-edit';
+	export default {
+		components: {
+			fileEdit
+		},
+
+		data() {
+			return {
+				tableList: [],
+				fileId: []
+
+			}
+		},
+		onLoad({
+			fileId
+		}) {
+			if (fileId) {
+				this.fileId = JSON.parse(fileId)
+			}
+			this.init()
+			uni.$off('setDocList')
+			uni.$on('setDocList', (id) => {
+				this.fileId.push(...id);
+				this.init()
+			})
+		},
+		methods: {
+			async init() {
+				if (this.fileId.length > 0) {
+					this.tableList = await queryIds({
+						ids: "'" + this.fileId + "'"
+					});
+				} else {
+					this.tableList = [];
+				}
+			},
+			del(index) {
+				this.tableList.splice(index, 1);
+				this.fileId = this.tableList.map((item) => item.id);
+			},
+			selectDoc() {
+				uni.navigateTo({
+					url: '/pages/doc/selectDoc?isAll=' + 1 + '&fileId=' + JSON.stringify(this.fileId)
+				})
+			},
+			save() {
+				uni.$emit('getFiles', this.fileId)
+				this.back()
+			},
+			onunload() {
+				uni.$off('setDocList')
+			},
+			add() {
+				this.$refs.fileEditRef.open()
+			},
+			done(id){
+				this.fileId.push(...id);
+				this.init()
+			}
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.listTitle {
+		width: 420rpx;
+		white-space: nowrap;
+		/* 防止文本换行 */
+		overflow: hidden;
+		/* 隐藏溢出的内容 */
+		text-overflow: ellipsis;
+		/* 显示省略符号来代表被修剪的文本 */
+	}
+
+	.topBtn {
+		display: flex;
+		margin-bottom: 20rpx;
+		margin-top: 10rpx;
+
+		uni-button {
+			width: 180rpx;
+			margin-left: 5px;
+			margin-right: 5px;
+		}
+	}
+
+	.listBtn {
+		display: flex;
+
+		uni-button {
+			width: 100rpx;
+			// height: 50rpx;
+			margin-left: 10rpx;
+			margin-right: 0;
+			color: #fff !important;
+			border: none;
+			background: #157a2c !important;
+
+		}
+	}
+
+	.footerButton {
+		width: 100%;
+		height: 84rpx;
+		display: flex;
+		position: fixed;
+		bottom: 0;
+		z-index: 10;
+
+		/deep/.u-button {
+			height: 100%;
+		}
+
+		>view {
+			flex: 1;
+
+		}
+	}
+
+	.item {
+		display: flex;
+		flex-direction: column;
+		padding: 16rpx;
+
+		>view {
+			padding-top: 14rpx;
+		}
+
+		.item_title {
+			font-size: 32rpx;
+			font-weight: 800;
+		}
+
+		.item_list {
+			display: flex;
+			align-items: center;
+
+			text {
+				font-size: 28rpx;
+			}
+
+			.lable {
+				color: #626060;
+			}
+
+			.value {
+				color: #333;
+				margin-left: 12rpx;
+				flex: 1
+			}
+		}
+	}
+
+	/deep/.u-swipe-action-item__right__button__wrapper {
+		background-color: #f56c6c !important;
+	}
+
+	/deep/.u-input {
+		padding: 0 !important;
+	}
+
+	/deep/uni-textarea {
+		padding: 0 !important;
+	}
+</style>

+ 265 - 0
pages/doc/file-edit.vue

@@ -0,0 +1,265 @@
+<template>
+	<u-popup :show="show" @close="cancel" :closeable="true">
+		<view style="width: 750rpx;">
+			<u-cell-group>
+				<u-cell title="编码分类">
+					<u--input slot="value" placeholder="请选择" border="surround" v-model="form.codeTypeName"
+						@click.native="codeTypeOpen"></u--input>
+				</u-cell>
+				<u-cell title="编码方案">
+					<uni-data-picker :map="{text:'name',value:'id'}" v-model="form.businessCodeId" slot="value"
+						placeholder="请选择" :localdata="options">
+					</uni-data-picker>
+				</u-cell>
+				<u-cell title="文档类型">
+					<uni-data-picker v-model="form.type" slot="value" placeholder="请选择" :localdata="doc_type">
+					</uni-data-picker>
+				</u-cell>
+				<u-cell title="文档">
+					<fileSelector class="fileList" slot="value" 
+						@filesChanged="onFilesChanged" />
+
+				</u-cell>
+				<u-cell title="文档位置">
+					<u--input slot="value" placeholder="请选择" border="surround" v-model="form.directoryName"
+						@click.native="directoryIdOpen">
+					</u--input>
+				</u-cell>
+			</u-cell-group>
+			<view class="btn">
+				<u-button type="primary" @click="cancel" text="返回"></u-button>
+				<u-button @click="save" text="保存"></u-button>
+			</view>
+
+
+		</view>
+		<ba-tree-picker ref="codeTypeRef" key="verify" :multiple="false" @select-change="codeTypeBack" title="选择编码分类"
+			:localdata="list" valueKey="id" textKey="name" childrenKey='sonDirectoryList' />
+		<ba-tree-picker ref="directoryIdRef" :multiple="false" @select-change="directoryIdBack" title="选择文档位置"
+			:localdata="folderList" valueKey="id" textKey="name" childrenKey='sonDirectoryList' />
+	</u-popup>
+</template>
+
+<script>
+	import {
+		fileSaveAPI,
+		selectTreeList,
+		listParentId,
+		getDocTreeListAPI,
+		listCode
+	} from './api/index';
+	import {
+		getByCode
+	} from '@/api/pda/common.js'
+	import baTreePicker from "@/components/ba-tree-picker/ba-tree-picker.vue"
+	// 引入组件
+	import fileSelector from '@/uni_modules/zhouquan-fileSelector/components/zhouquan-fileSelector/file-selector.vue';
+	export default {
+		components: {
+			baTreePicker,
+			fileSelector
+		},
+		data() {
+			const defaultForm = {
+				name: '', //名称
+				type: '', //类型
+				sizeUnit: '', //大小,
+				unit: '', //单位
+				remark: '', //备注
+				status: '', //状态
+				storagePathId: '',
+				directoryId: '',
+				businessCodeId: '',
+				storagePath: [],
+				id: '',
+				lcyStatus: 1,
+				fileType: 0,
+				codeTypeName: '',
+				directoryName: '',
+
+			};
+			return {
+				folderList: [],
+				files: [],
+				// 表单数据
+				form: {
+					...defaultForm
+				},
+				list: [],
+				options: [],
+				show: false,
+				doc_type: []
+			};
+		},
+
+		async created() {
+			this.userInfo = uni.getStorageSync('userInfo')
+			let query = {
+				type: 0,
+				currentUserId: this.userInfo.userId
+			};
+			this.folderList = await getDocTreeListAPI(query);
+			const doc_type = await getByCode('doc_type');
+			this.doc_type = doc_type.map(item => {
+				const key = Object.keys(item)[0]
+				return {
+					value: key,
+					text: item[key]
+				}
+			})
+		},
+		methods: {
+			async open() {
+
+				this.show = true;
+				this.list = await selectTreeList();
+				this.options = await listCode();
+				if (this.options.length > 0) {
+					this.form.businessCodeId = this.options[0].id
+				}
+				this.setTree(this.list);
+			},
+			setTree(data) {
+				data.forEach((item) => {
+					item.sonDirectoryList = item.sonDirectoryList.filter(
+						(item) => item.type == 1
+					);
+					if (item.sonDirectoryList.length > 0) {
+						this.setTree(item.sonDirectoryList);
+					}
+				});
+			},
+
+			codeTypeOpen() {
+				this.$refs.codeTypeRef._show()
+			},
+			async codeTypeBack(data, name) {
+				let list = await listParentId({
+					pageNum: 1,
+					size: 100,
+					parentId: data[0]
+				});
+				this.form.codeTypeName = name
+				this.form.codeType = data[0]
+				this.options = list.list.filter((item) => item.type == 2);
+				this.form.businessCodeId = '';
+			},
+			uploadFile(list) {
+				let PromiseAll = []
+				const apiUrl = this.apiUrl
+				const token = uni.getStorageSync("token"); //取存本地的token
+				list.forEach(item => {
+					PromiseAll.push(
+						new Promise((resolve, reject) => {
+							uni.uploadFile({
+								url: apiUrl + '/main/file/uploadFile',
+								filePath: item.path,
+								name: 'multiPartFile',
+								header: {
+									authorization: token
+								},
+								success: (uploadFileRes) => {
+									let data = JSON.parse(uploadFileRes.data)
+									resolve(data.data)
+								}
+							});
+						}),
+					)
+				})
+				return Promise.all(PromiseAll)
+			},
+			directoryIdOpen() {
+				this.$refs.directoryIdRef._show()
+			},
+			directoryIdBack(data, name) {
+
+				this.form.directoryName = name
+				this.form.directoryId = data[0]
+
+			},
+
+			// 当文件发生变化时触发
+			onFilesChanged(fileList) {
+				this.files = fileList
+				// console.log('文件列表:', fileList)
+			},
+			/* 保存编辑 */
+			async save() {
+				uni.showLoading({
+					title: '加载中'
+				})
+
+				this.form.storagePath = await this.uploadFile(this.files)
+				fileSaveAPI(this.form)
+					.then((msg) => {
+						uni.hideLoading()
+						console.log(msg,'msg')
+						
+						this.$emit('success', msg);
+						this.cancel()
+					})
+					.catch((e) => {
+						uni.hideLoading()
+						this.cancel()
+						
+					});
+			},
+
+			cancel() {
+				this.form = {
+					...this.defaultForm
+				};
+				this.files = []
+				this.show = false;
+			}
+		}
+	};
+</script>
+<style scoped lang="scss">
+	.aaa {
+		width: 100%;
+
+		::v-deep .upload-demo {
+			width: 100%;
+
+			.el-upload--text {
+				width: 100%;
+
+				button {
+					width: 100%;
+					background: #ffffff;
+					border: 1px solid #dbdbdb;
+					border-radius: 5px;
+				}
+			}
+
+			.el-upload-list {
+				transform: translate(10px, -39px);
+				position: absolute;
+			}
+		}
+	}
+
+	.fileList {
+		padding: 0;
+
+		/deep/.upload-area {
+			height: 90rpx;
+			width: 140rpx;
+		}
+
+		/deep/.file-item {
+			padding: 0;
+			margin-bottom: 8rpx;
+		}
+	}
+	.btn{
+		margin-top: 16rpx;
+		margin-bottom: 10rpx;
+		display: flex;
+		uni-bottom{
+			width: 160rpx;
+		}
+		
+	}
+</style>

+ 263 - 0
pages/doc/file-edit1.vue

@@ -0,0 +1,263 @@
+<!-- 用户编辑弹窗 -->
+<template>
+  <ele-modal
+    width="500px"
+    :visible.sync="showEditFlag"
+    :close-on-click-modal="false"
+    custom-class="ele-dialog-form"
+    append-to-body
+    @close="cancel"
+    :title="title"
+    ref="Emodal"
+  >
+    <el-form ref="form" :model="form" :rules="rules" label-width="82px">
+      <el-row :gutter="15">
+        <el-col :span="24">
+          <el-form-item label="编码分类" prop="codeType">
+            <ele-tree-select
+              clearable
+              :data="list"
+              v-model="form.codeType"
+              placeholder="请选择"
+              default-expand-all
+              labelKey="name"
+              childrenKey="sonDirectoryList"
+              valueKey="id"
+              @change="typeChange"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="编码方案" prop="businessCodeId">
+            <el-select
+              v-model="form.businessCodeId"
+              placeholder="请选择"
+              style="width: 100%"
+            >
+              <el-option
+                v-for="item in options"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="文档类型" prop="type">
+            <DictSelection
+              dictName="文档类型"
+              v-model="form.type"
+            ></DictSelection>
+          </el-form-item>
+        </el-col>
+
+        <el-col :span="24">
+          <el-form-item label="文档" prop="storagePath">
+            <div style="display: flex; align-items: center">
+              <fileUpload
+                v-model="form.storagePath"
+                module="main"
+                :limit="100"
+                :multiple="true"
+                @fileChange="fileChange"
+              >
+
+              </fileUpload>
+            </div>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="文档位置" prop="directoryId">
+            <el-cascader
+              style="width: 100%"
+              v-model="form.directoryId"
+              :options="folderList"
+              :props="{
+                value: 'id',
+                label: 'name',
+                children: 'sonDirectoryList',
+                emitPath: false,
+                checkStrictly: true
+              }"
+            ></el-cascader>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="备注" prop="remark">
+            <el-input
+              v-model="form.remark"
+              type="textarea"
+              placeholder="请输入"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template v-slot:footer>
+      <el-button @click="cancel">取消</el-button>
+      <el-button type="primary" :loading="loading" @click="save">
+        确认
+      </el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+import { fileSaveAPI,selectTreeList, listParentId,getDocTreeListAPI,listCode} from './api/index';
+import FileUpload from './fileUpload.vue';
+import { setFolderList } from './util.js';
+import { mapGetters } from 'vuex';
+export default {
+  components: { FileUpload },
+
+  data() {
+    const defaultForm = {
+      name: '', //名称
+      type: '', //类型
+      sizeUnit: '', //大小,
+      unit: '', //单位
+      remark: '', //备注
+      status: '', //状态
+      storagePathId: '',
+      directoryId: '',
+      businessCodeId: '',
+      storagePath: [],
+      id: '',
+      lcyStatus: 1,
+      fileType:0
+    };
+    return {
+      rules: {
+        businessCodeId: [
+          { required: true, message: '请选择', trigger: 'blur' }
+        ],
+        // codeType: [{ required: true, message: '请选择', trigger: 'blur' }],
+        directoryId: [{ required: true, message: '请选择', trigger: 'blur' }],
+        storagePath: [{ required: true, message: '请选择', trigger: 'blur' }]
+      },
+      templateVisible: false,
+      folderList: [],
+      list: [],
+      options: [],
+      defaultForm,
+      code: '',
+      // 表单数据
+      form: { ...defaultForm },
+
+      // 提交状态
+      loading: false,
+      showEditFlag: false,
+      title: '',
+      type: '新建附件'
+    };
+  },
+  computed: {
+    // 是否开启响应式布局
+    styleResponsive() {
+      return this.$store.state.theme.styleResponsive;
+    },
+    ...mapGetters(['user'])
+  },
+  async created() {
+    let query = {
+      type: 0,
+      currentUserId: this.user.info.userId
+    };
+    this.folderList = await getDocTreeListAPI(query);
+    setFolderList(this.folderList); //权限过滤
+  },
+  methods: {
+    async open() {
+    
+      this.showEditFlag = true;
+      this.list = await selectTreeList();
+      this.options= await listCode();
+      if(this.options.length>0){
+        this.form.businessCodeId=this.options[0].id
+      }
+      this.setTree(this.list);
+    },
+    setTree(data) {
+      data.forEach((item) => {
+        item.sonDirectoryList = item.sonDirectoryList.filter(
+          (item) => item.type == 1
+        );
+        if (item.sonDirectoryList.length > 0) {
+          this.setTree(item.sonDirectoryList);
+        }
+      });
+    },
+    async typeChange(val) {
+      let data = await listParentId({
+        pageNum: 1,
+        size: 100,
+        parentId: val
+      });
+      this.options = data.list.filter((item) => item.type == 2);
+      this.form.businessCodeId = '';
+    },
+
+    fileChange(file) {
+      this.form.name = file.name.replace(/\.[^/.]+$/, '');
+    },
+
+    /* 保存编辑 */
+    save() {
+      this.$refs.form.validate(async (valid) => {
+        if (!valid) {
+          return false;
+        }
+
+        const data = {
+          ...this.form,
+        };
+
+        this.loading = true;
+        fileSaveAPI(data)
+          .then( (msg) => {
+            this.loading = false;
+
+            this.cancel();
+            this.$emit('done',msg);
+          })
+          .catch((e) => {
+            this.loading = false;
+          });
+      });
+    },
+
+    cancel() {
+      this.form = { ...this.defaultForm };
+      this.$refs.form.clearValidate();
+      this.showEditFlag = false;
+    }
+  }
+};
+</script>
+<style scoped lang="scss">
+.aaa {
+  width: 100%;
+
+  ::v-deep .upload-demo {
+    width: 100%;
+
+    .el-upload--text {
+      width: 100%;
+
+      button {
+        width: 100%;
+        background: #ffffff;
+        border: 1px solid #dbdbdb;
+        border-radius: 5px;
+      }
+    }
+
+    .el-upload-list {
+      transform: translate(10px, -39px);
+      position: absolute;
+    }
+  }
+}
+</style>

+ 95 - 0
pages/doc/index.vue

@@ -0,0 +1,95 @@
+<template>
+	<div class="doc">
+		<view class="box">
+			<u-badge :value="value.length">
+
+
+			</u-badge>
+		</view>
+
+		<u-button :plain="true" :hairline="true" v-if="type != 'view'" size='mini' text="上传"
+			@click="handleUpload"></u-button>
+		<u-button :plain="true" :hairline="true" v-else size='mini' text="查看" @click="handleUpload"></u-button>
+	</div>
+</template>
+
+<script>
+	export default {
+		name: 'index',
+
+		model: {
+			prop: 'value',
+			event: 'updateVal'
+		},
+		props: {
+			type: {
+				type: String,
+				default: ''
+			},
+			value: {
+				type: Array,
+				default: () => {
+					return [];
+				}
+			}
+		},
+		data() {
+			return {
+				selectVal: ''
+			};
+		},
+		watch: {
+			value: {
+				handler(newVal) {
+
+					this.selectVal = newVal || [];
+				},
+				immediate: true
+			}
+		},
+		created() {
+			uni.$off('getFiles')
+			uni.$on('getFiles', (id) => {
+				this.getFiles(id)
+			})
+		},
+		destroyed() {
+			uni.$off('getFiles')
+		},
+		methods: {
+			handleUpload() {
+				uni.navigateTo({
+					url: '/pages/doc/docList?fileId=' + JSON.stringify(this.selectVal)
+				})
+			},
+			getFiles(val = []) {
+				this.$emit('updateVal', val);
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	.doc {
+		position: relative;
+
+		.box {
+			position: absolute;
+			right: -20rpx;
+			top: -15rpx;
+			width: 40rpx;
+			z-index: 999;
+		}
+
+		uni-button {
+			width: 100rpx;
+			// height: 50rpx;
+			margin-left: 10rpx;
+			margin-right: 0;
+			color: #fff !important;
+			border: none;
+			background: #157a2c !important;
+
+		}
+	}
+</style>

+ 390 - 0
pages/doc/selectDoc.vue

@@ -0,0 +1,390 @@
+<template>
+	<view class="mainBox">
+		<uni-nav-bar fixed="true" statusBar="true" left-icon="back" title="选择" @clickLeft="back">
+			<!--右菜单-->
+			<template slot="right">
+				<u-button type="success" size="small" class="u-reset-button" @click="$refs.treePicker._show()"
+					text="文件夹"></u-button>
+			</template>
+		</uni-nav-bar>
+
+		<view class="wrapper">
+			<u-list class="listContent">
+				<checkbox-group v-for="(item, index) in listData" :key="index" @change="e => selectVal(e, item, index)">
+					<label>
+						<view class="listBox">
+							<view class="listBox-sel">
+								<checkbox :value="item.code" color="#fff" :disabled="item.disabled"
+									:checked="item.checked" />
+							</view>
+							<view class="listBox-con">
+								<view class="listBox-top">
+									<view class="listBox-name">
+										{{ item.name }}
+									</view>
+									<view class="listBox-code">
+										{{ item.code }}
+									</view>
+								</view>
+								<view class="listBox-bottom">
+									<view>文件名称:{{ item.storagePath[0].name }}</view>
+									<view>版本:{{ item.version }}</view>
+									<view>创建人:{{ item.createUserName }}</view>
+									<view>创建时间:{{ item.createTime }}</view>
+
+								</view>
+							</view>
+						</view>
+					</label>
+				</checkbox-group>
+				<u-empty class="noDate" style="margin-top: 20vh" v-if="!listData.length"></u-empty>
+			</u-list>
+		</view>
+
+		<view class="footer">
+			<view class="bottom" v-if="this.isAll==1">
+				<checkbox v-if="!seletedAll" color="#fff" :checked="seletedAll" @tap="_seletedAll">全选</checkbox>
+				<checkbox class="select-all" color="#fff" v-else :checked="seletedAll" @tap="_seletedAll">取消全选
+				</checkbox>
+			</view>
+			<u-button type="success" size="small" class="u-reset-button" :disabled="!checkListLen" @click="jumpAdd">
+				<view class="selBtn">选择( {{ checkListLen }} )</view>
+			</u-button>
+		</view>
+		<ba-tree-picker ref="treePicker" key="verify" :multiple="false" @select-change="confirm" title="选择文件夹"
+			:localdata="classificationList" valueKey="id" textKey="name" childrenKey='sonDirectoryList' />
+	</view>
+</template>
+
+<script>
+	import {
+		getDocTreeListAPI,
+		filePageAPI
+	} from './api';
+
+
+
+	import baTreePicker from '@/components/ba-tree-picker/ba-tree-picker.vue'
+	export default {
+		components: {
+			baTreePicker
+		},
+
+		data() {
+			return {
+
+				pickTabIndex: 1,
+				popupShow: false, //右侧搜索窗
+				typeIndex: 1,
+				listData: [], //列表数据
+				classificationList: [], //分类数据
+				seletedAll: false, //全选状态,
+				isAll: '',
+				fileId:[]
+
+			}
+		},
+		//选择的列表长度
+		computed: {
+
+			checkListLen() {
+
+				return this.listData.filter(el => el.checked).length
+			},
+
+		},
+		onLoad({
+			isAll,fileId
+		}) {
+			this.isAll = isAll //1多选 2单选
+			this.fileId=JSON.parse(fileId)
+			this.userInfo = uni.getStorageSync('userInfo')
+			this.getClassify()
+		},
+		onShow() {},
+		methods: {
+
+			//列表数据
+			async getList() {
+				this._getClassifyList()
+			},
+			async _getClassifyList() {
+				uni.showLoading({
+					title: '数据加载中'
+				})
+				const params = {
+					directoryId: this.categoryLevelId,
+					fileType: 0,
+					lcyStatus: "1"
+				}
+				filePageAPI(params).then(res => {
+					uni.hideLoading()
+
+					this.listData = res.list.map(item=>{
+						if(this.fileId.includes(item.id)){
+							item['disabled']=true
+						}
+						return item
+					})
+				})
+			},
+			confirm([id]) {
+				this.categoryLevelId = id
+
+				this.getList()
+			},
+			async getClassify() {
+				getDocTreeListAPI({
+					type: 0,
+					currentUserId: this.userInfo.userId
+				}).then(res => {
+					this.classificationList = res
+					this.categoryLevelId=res[0].id
+					console.log(this.classificationList, 'this.classificationList')
+
+					this.getList()
+				});
+			},
+
+			//勾选
+			selectVal(e, val, index) {
+
+				this.$set(this.listData[index], 'checked', !this.listData[index].checked)
+
+				if (this.isAll != 1) {
+					this.listData.forEach((item, i) => {
+						if (item.id != val.id) {
+							this.$set(this.listData[i], 'checked', false)
+						}
+					})
+				} else {
+
+					this.seletedAll = !this.listData.some(item => !item.checked)
+				}
+
+			},
+			//全选按钮
+			_seletedAll() {
+				if (!this.seletedAll) {
+					this.seletedAll = true
+					this.listData.map(item => {
+						this.$set(item, 'checked', true)
+					})
+				} else {
+					this.seletedAll = false
+					this.listData.map(item => {
+						this.$set(item, 'checked', false)
+					})
+				}
+			},
+			//跳转回添加页面
+			jumpAdd() {
+				uni.$emit(
+					'setDocList',
+					this.listData.filter(item => item.checked).map(item=>item.id)
+				)
+
+				uni.navigateBack()
+			},
+			//返回添加页
+			backAdd() {
+				uni.navigateBack()
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.mainBox {
+		height: 100vh;
+		display: flex;
+		flex-direction: column;
+
+		.wrapper {
+			flex: 1;
+			overflow: hidden;
+		}
+
+		.searchBox {
+			padding: 10rpx 0;
+			box-sizing: border-box;
+			background-color: #dedede;
+			height: 90rpx;
+			width: 100%;
+			line-height: 90rpx;
+			display: flex;
+			justify-content: space-around;
+			align-items: center;
+
+			input {
+				height: 78rpx;
+				width: 65%;
+				background: #f9f9f9 !important;
+				margin: 0 10rpx;
+				padding: 0 10rpx;
+				box-sizing: border-box;
+				border-radius: 5rpx;
+			}
+
+			.searchBtn {
+				height: 80rpx;
+				background: #f9f9f9 !important;
+				color: #676767;
+				font-size: 28rpx;
+				padding: 0 42rpx;
+				box-sizing: border-box;
+				outline: none;
+				border: none;
+				width: 260rpx;
+
+				.icon-sousuo {
+					font-size: 22px;
+				}
+
+				.text {
+					font-size: 16px;
+					margin-left: 10px;
+				}
+			}
+		}
+
+		.tab-title {
+			position: fixed;
+			top: 190rpx;
+			z-index: 99;
+			width: 100%;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			height: $tab-height;
+			line-height: $tab-height;
+			background-color: #ffffff;
+			border-bottom: 2rpx solid #f2f2f2;
+			box-sizing: border-box;
+
+			.tab-item {
+				width: 25%;
+				text-align: center;
+				font-size: 32rpx;
+				color: $uni-text-color-grey;
+			}
+
+			.tab-item.active {
+				color: $j-primary-border-green;
+				border-bottom: 1px solid $j-primary-border-green;
+				font-weight: bold;
+			}
+
+			.tab-item.filter {
+				flex: 1;
+				padding: 0px 30rpx;
+
+				.uni-icons {
+					display: flex;
+					padding-top: 5px;
+				}
+			}
+
+			.screenIcon {
+				display: flex;
+				width: 80px;
+				justify-content: center;
+
+				.screenText {
+					font-size: 32rpx;
+					color: $uni-text-color-grey;
+				}
+			}
+		}
+
+		.listContent {
+			height: 100% !important;
+
+			.listBox {
+				display: flex;
+				// height: 180rpx;
+				padding: 20rpx 0;
+				border-bottom: 2rpx solid #e5e5e5;
+
+				.listBox-sel {
+					height: 90rpx;
+					width: 80rpx;
+					// line-height: 90rpx;
+					text-align: center;
+
+					checkbox {
+						transform: scale(1.2);
+					}
+				}
+
+				.listBox-con {
+					width: 100%;
+					// display: flex;
+					// flex-wrap: wrap;
+					// justify-content: space-between;
+					align-items: center;
+					padding: 0 18rpx 0 0;
+
+					.listBox-top {
+						width: 100%;
+						display: flex;
+						justify-content: space-between;
+						padding-bottom: 10rpx;
+
+						.listBox-name,
+						.listBox-code {
+							display: inline-block;
+							font-size: $uni-font-size-sm;
+							font-weight: bold;
+						}
+					}
+
+					.listBox-bottom {
+						width: 100%;
+						display: flex;
+						justify-content: space-between;
+						font-size: $uni-font-size-sm;
+						flex-wrap: wrap;
+
+						>view {
+							width: 50%;
+							overflow: hidden;
+							white-space: nowrap;
+							text-overflow: ellipsis;
+						}
+					}
+				}
+			}
+
+			.noDate {
+				height: 100%;
+			}
+		}
+
+		//底部按钮
+		.footer {
+			height: 90rpx;
+			position: relative;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			bottom: 0;
+			width: 100%;
+			height: 100rpx;
+			border-top: 1rpx solid #eeecec;
+			background-color: #ffffff;
+			z-index: 999;
+
+			.bottom {
+				margin-left: 10rpx;
+			}
+
+			.u-reset-button {
+				position: absolute;
+				right: 10rpx;
+				top: 20rpx;
+				width: 150rpx;
+			}
+		}
+	}
+</style>

+ 1 - 1
pages/saleManage/businessOpportunity/add.vue

@@ -306,6 +306,6 @@
 	}
 
 	/deep/.u-subsection__item__text {
-		font-size: 24rpx !important;
+		font-size: 28rpx !important;
 	}
 </style>

+ 36 - 13
pages/saleManage/contact/add.vue

@@ -1,6 +1,7 @@
 <template>
 	<view class="mainBox">
-		<uni-nav-bar background-color="#157A2C" color="#fff" fixed="true" statusBar="true" left-icon="back" :title="this.form.id?'修改客户':'新建客户'" @clickLeft="back">
+		<uni-nav-bar background-color="#157A2C" color="#fff" fixed="true" statusBar="true" left-icon="back"
+			:title="this.form.id?'修改客户':'新建客户'" @clickLeft="back">
 		</uni-nav-bar>
 		<u-subsection :list="list" :current="current" @change="sectionChange"></u-subsection>
 		<u-cell-group v-show='current==0'>
@@ -100,10 +101,15 @@
 			<u-cell title="收件人详细地址" arrow-direction="down">
 				<u--textarea slot="value" placeholder="请输入" border="surround" v-model="otherForm.address"></u--textarea>
 			</u-cell>
+
 			<u-cell title="经营范围" arrow-direction="down">
 				<u--textarea slot="value" placeholder="请输入" border="surround"
 					v-model="form.businessScope"></u--textarea>
 			</u-cell>
+			<u-cell title="营业执照附件" arrow-direction="down">
+				<fileMain slot="value" v-model="form.businessLicenseFile"></fileMain>
+			</u-cell>
+
 			<u-cell title="备注" arrow-direction="down">
 				<u--textarea slot="value" placeholder="请输入" border="surround" v-model="form.remark"></u--textarea>
 			</u-cell>
@@ -123,6 +129,7 @@
 
 <script>
 	import baTreePicker from "@/components/ba-tree-picker1/ba-tree-picker.vue"
+	import fileMain from "@/pages/doc/index.vue"
 	import bankList from "./components/bankList.vue"
 	import linkList from "./components/linkList.vue"
 	import {
@@ -136,14 +143,16 @@
 	} from '@/api/pda/common.js'
 	import {
 		contactSave,
-		contactDetail,getInfoByIds
+		contactDetail,
+		getInfoByIds
 	} from '@/api/saleManage/contact/index.js'
 	const chinaData = require("@/static/json/regions-data.json")
 	export default {
 		components: {
 			baTreePicker,
 			bankList,
-			linkList
+			linkList,
+			fileMain
 		},
 		data() {
 			return {
@@ -228,13 +237,13 @@
 					this.otherForm = res.other
 					this.bankList = res.bankList
 					this.linkList = res.linkList
-					
-					this.address=this.form.addressId&&this.form.addressId.split(',')
-					this.address1=this.otherForm.addressId&&this.otherForm.addressId.split(',')
-					this.form.industry=this.form.industryCode&&this.form.industryCode.split(',')
-					this.form.level=this.form.level+''
-					this.otherForm.settlementMode=this.otherForm.settlementMode+''
-					this.categoryIdList=this.form.categoryId&&this.form.categoryId.split(',')
+
+					this.address = this.form.addressId && this.form.addressId.split(',')
+					this.address1 = this.otherForm.addressId && this.otherForm.addressId.split(',')
+					this.form.industry = this.form.industryCode && this.form.industryCode.split(',')
+					this.form.level = this.form.level + ''
+					this.otherForm.settlementMode = this.otherForm.settlementMode + ''
+					this.categoryIdList = this.form.categoryId && this.form.categoryId.split(',')
 					getInfoByIds(this.form.categoryId).then((res) => {
 						let categoryName = res.map((item) => item.name)?.join(',');
 						this.$set(this.form, 'categoryName', categoryName);
@@ -276,7 +285,7 @@
 			},
 			//监听选择(ids为数组)
 			selectChange(data = []) {
-				this.categoryIdList=data.map(item => item.id)
+				this.categoryIdList = data.map(item => item.id)
 				this.form.categoryId = data.map(item => item.id).toString()
 				this.form.categoryName = data.map(item => item.name).toString()
 				// console.log(data)
@@ -387,10 +396,11 @@
 		position: fixed;
 		bottom: 0;
 		z-index: 10;
-		
+
 		/deep/.u-button {
 			height: 100%;
 		}
+
 		>view {
 			flex: 1;
 
@@ -402,6 +412,19 @@
 	}
 
 	/deep/.u-subsection__item__text {
-		font-size: 24rpx !important;
+		font-size: 28rpx !important;
+	}
+
+	/deep/.u-cell__body {
+		// {
+		// 	font-size: 26rpx !important;
+		// }
+		// padding: 12rpx 12rpx;
+
+		.u-cell__title-text,
+		.input-value,
+		.uni-input-wrapper {
+			font-size: 26rpx !important;
+		}
 	}
 </style>

+ 1 - 1
pages/saleManage/contact/components/bankListAdd.vue

@@ -100,6 +100,6 @@
 	// }
 
 	/deep/.u-subsection__item__text {
-		font-size: 24rpx !important;
+		font-size: 28rpx !important;
 	}
 </style>

+ 58 - 23
pages/saleManage/contact/components/comment.vue

@@ -65,7 +65,7 @@
 			</view>
 			<view @click="chooseImage" style="display: flex;">
 				<text>现场照片:</text>
-				<u-button type="primary" style="width: 150rpx;" size="small" text="点击上传"></u-button>
+				<u-button type="primary" style="width: 150rpx;" size="small" text="拍照上传"></u-button>
 			</view>
 			<view>
 				<u-album :urls="imgs" :singleSize="180" :multipleSize="180"></u-album>
@@ -106,46 +106,81 @@
 		methods: {
 			chooseImage() {
 				const _this = this
-				const token = uni.getStorageSync("token"); //取存本地的token
 				uni.chooseImage({
 					count: 9, //默认9
 					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
 					sourceType: ['camera'], //从相册选择
 					success: function(res) {
-						const tempFilePaths = res.tempFilePaths;
-						tempFilePaths.forEach(item => {
+						uni.showLoading({
+							title: '加载中'
+						})
+
+						_this.uploadFile(res.tempFilePaths).then(res => {
+							res.forEach(item => {
+								let fileNames = item.storePath.split('/')
+								let url = _this.apiUrl +
+									'/main/file/getFile?objectName=' +  item.storePath+
+									'&fullfilename=' + fileNames[fileNames.length -
+										1]
+								_this.imgs.push(url)
+							})
+							uni.hideLoading()
+
+						})
+						// tempFilePaths.forEach(item => {
+						// 	uni.uploadFile({
+						// 		url: _this.apiUrl + '/main/file/upload',
+						// 		filePath: item,
+						// 		name: 'multiPartFile',
+						// 		header: {
+						// 			authorization: token
+						// 		},
+
+						// 		success: (uploadFileRes) => {
+						// 			let data = JSON.parse(uploadFileRes.data)
+						// 			if (data?.data) {
+						// 				let fileNames = data?.data.storePath.split('/')
+						// 				let url = _this.apiUrl +
+						// 					'/main/file/getFile?objectName=' + data?.data
+						// 					.storePath +
+						// 					'&fullfilename=' + fileNames[fileNames.length -
+						// 						1]
+						// 				_this.imgs.push(url)
+						// 			}
+						// 			console.log(_this.imgs, '_this.imgs')
+
+						// 		}
+						// 	});
+						// })
+					}
+				});
+			},
+			uploadFile(list) {
+				let PromiseAll = []
+				const apiUrl = this.apiUrl
+				const token = uni.getStorageSync("token"); //取存本地的token
+				list.forEach(item => {
+					PromiseAll.push(
+						new Promise((resolve, reject) => {
 							uni.uploadFile({
-								url: _this.apiUrl + '/main/file/upload',
+								url: apiUrl + '/main/file/upload',
 								filePath: item,
 								name: 'multiPartFile',
 								header: {
 									authorization: token
 								},
-
 								success: (uploadFileRes) => {
 									let data = JSON.parse(uploadFileRes.data)
-									if (data?.data) {
-										let fileNames = data?.data.storePath.split('/')
-										let url = _this.apiUrl +
-											'/main/file/getFile?objectName=' + data?.data
-											.storePath +
-											'&fullfilename=' + fileNames[fileNames.length -
-												1]
-										_this.imgs.push(url)
-									}
-									console.log(_this.imgs, '_this.imgs')
-
+									resolve(data.data)
 								}
 							});
-						})
-
+						}),
+					)
+				})
 
+				return Promise.all(PromiseAll)
 
-					}
-				});
 			},
-
-
 			/**
 			 * 点击取消按钮
 			 */

+ 1 - 1
pages/saleManage/contact/components/followListAdd.vue

@@ -165,6 +165,6 @@
 	// }
 
 	/deep/.u-subsection__item__text {
-		font-size: 24rpx !important;
+		font-size: 28rpx !important;
 	}
 </style>

+ 1 - 1
pages/saleManage/contact/components/linkListAdd.vue

@@ -162,6 +162,6 @@
 	// }
 
 	/deep/.u-subsection__item__text {
-		font-size: 24rpx !important;
+		font-size: 28rpx !important;
 	}
 </style>

+ 1 - 2
pages/saleManage/saleOrder/add.vue

@@ -783,8 +783,7 @@
 		}
 	}
 
-
 	/deep/.u-subsection__item__text {
-		font-size: 24rpx !important;
+		font-size: 28rpx !important;
 	}
 </style>

+ 4 - 0
uni_modules/zhouquan-fileSelector/changelog.md

@@ -0,0 +1,4 @@
+## 1.0.1(2025-02-08)
+修改APP端存放路径名称
+## 1.0.0(2025-02-08)
+文件选择器组件、多文件选择功能、文件类型识别功能(支持图片、文档、音频、视频、压缩包等)、文件列表展示功能、文件删除功能、H5平台支持、APP平台支持。

+ 266 - 0
uni_modules/zhouquan-fileSelector/components/zhouquan-fileSelector/file-selector.vue

@@ -0,0 +1,266 @@
+<template>
+    <view class="main">
+        <!-- 上传区域 -->
+        <view class="upload-area" @click="fileRender.createFileInputDom">
+            <view class="upload-icon">
+                <text class="icon-plus">+</text>
+            </view>
+            <text class="upload-text">上传文件</text>
+        </view>
+        
+        <!-- 文件列表 -->
+        <view class="file-list">
+            <view v-for="(file, index) in fileList" :key="index" class="file-item">
+                <text class="file-type-icon">
+                    {{getFileEmoji(file.name)}}
+                </text>
+                <view class="file-info">
+                    <text class="file-name">{{file.name}}</text>
+                </view>
+                <view class="delete-btn" @click="removeFile(index)">
+                    <text class="delete-icon">×</text>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+export default {
+    name: 'FileSelector',
+    
+    data() {
+        return {
+            fileList: [],
+            // 文件扩展名映射
+            fileTypes: {
+                image: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'],
+                document: ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'txt'],
+                audio: ['mp3', 'wav', 'ogg', 'aac'],
+                video: ['mp4', 'avi', 'mov', 'wmv', 'flv'],
+                archive: ['zip', 'rar', '7z', 'tar', 'gz'],
+                json: ['json']
+            }
+        }
+    },
+
+    methods: {
+        // 获取文件类型
+        getFileType(fileName) {
+            const extension = fileName.split('.').pop().toLowerCase()
+            for (let type in this.fileTypes) {
+                if (this.fileTypes[type].includes(extension)) return type
+            }
+            return 'other'
+        },
+
+        // 根据文件类型获取对应emoji
+        getFileEmoji(fileName) {
+            const fileType = this.getFileType(fileName)
+            const emojiMap = {
+                image: '🖼️',
+                document: '📄',
+                audio: '🎵',
+                video: '🎬',
+                archive: '📦',
+                json: '📋',
+                other: '📎'
+            }
+            return emojiMap[fileType] || '📎'
+        },
+
+        // 接收文件
+        async receiveRenderFile(result) {
+            let fileInfo = {
+                name: result.name,
+                path: ''
+            }
+
+            //#ifdef APP-PLUS
+            const fileUrl = await this.base64toPath(result.filePath, result.name)
+            fileInfo.path = fileUrl.localAbsolutePath
+            //#endif
+            //#ifdef H5
+            fileInfo.path = result.filePath
+            //#endif
+
+            this.fileList.push(fileInfo)
+            this.$emit('filesChanged', this.fileList)
+            this.$emit('fileSelected', fileInfo)
+        },
+
+        // 删除文件
+        removeFile(index) {
+            this.fileList.splice(index, 1)
+            this.$emit('filesChanged', this.fileList)
+        },
+
+        // APP端base64转本地路径
+        async base64toPath(base64, attachName) {
+            return new Promise((resolve, reject) => {
+                const filePath = `_doc/file/${attachName}`
+                plus.io.resolveLocalFileSystemURL('_doc', entry => {
+                    entry.getDirectory('file', {
+                        create: true,
+                        exclusive: false
+                    }, entry => {
+                        entry.getFile(attachName, {
+                            create: true,
+                            exclusive: false
+                        }, entry => {
+                            entry.createWriter(writer => {
+                                writer.onwrite = () => {
+                                    resolve({
+                                        relativePath: filePath,
+                                        localAbsolutePath: plus.io.convertLocalFileSystemURL(filePath)
+                                    })
+                                }
+                                writer.onerror = reject
+                                writer.seek(0)
+                                writer.writeAsBinary(this.getSymbolAfterString(base64, ','))
+                            }, reject)
+                        }, reject)
+                    }, reject)
+                }, reject)
+            })
+        },
+
+        getSymbolAfterString(val, symbolStr) {
+            return val ? val.toString().split(symbolStr)[1] || val : ''
+        }
+    }
+}
+</script>
+
+<script module="fileRender" lang="renderjs">
+export default {
+    methods: {
+        createFileInputDom(e, ownerVm) {
+            const fileInput = document.createElement('input')
+            fileInput.setAttribute('type', 'file')
+            fileInput.setAttribute('accept', '*')
+            fileInput.setAttribute('multiple', 'multiple')
+            fileInput.click()
+
+            fileInput.addEventListener('change', e => {
+                if (!e.target.files.length) return
+
+                Array.from(e.target.files).forEach(file => {
+                    //#ifdef H5
+                    ownerVm.callMethod('receiveRenderFile', {
+                        name: file.name,
+                        filePath: URL.createObjectURL(file)
+                    })
+                    //#endif
+
+                    //#ifdef APP-PLUS
+                    const reader = new FileReader()
+                    reader.onload = ({target}) => {
+                        target?.result && ownerVm.callMethod('receiveRenderFile', {
+                            name: file.name,
+                            filePath: target.result
+                        })
+                    }
+                    reader.readAsDataURL(file)
+                    //#endif
+                })
+            })
+        }
+    }
+}
+</script>
+
+<style>
+/* 基础样式 */
+.main {
+    padding: 20rpx;
+}
+
+/* 上传区域样式 */
+.upload-area {
+    width: 100%;
+    height: 160rpx;
+    background-color: #FFFFFF;
+    border: 2rpx dashed #E5E5E5;
+    border-radius: 8rpx;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    margin-bottom: 20rpx;
+    padding: 20rpx;
+    box-sizing: border-box;
+}
+
+.upload-icon {
+    width: 44rpx;
+    height: 44rpx;
+    background-color: #F7F7F7;
+    border-radius: 4rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-bottom: 8rpx;
+}
+
+.icon-plus {
+    color: #C7C7C7;
+    font-size: 28rpx;
+    font-weight: 100;
+}
+
+.upload-text {
+    font-size: 22rpx;
+    color: #A8A8A8;
+}
+
+/* 文件列表样式 */
+.file-list {
+    width: 100%;
+}
+
+.file-item {
+    background-color: #FFFFFF;
+    border-radius: 8rpx;
+    padding: 20rpx;
+    margin-bottom: 16rpx;
+    display: flex;
+    align-items: center;
+}
+
+.file-type-icon {
+    width: 44rpx;
+    height: 44rpx;
+    margin-right: 16rpx;
+    font-size: 32rpx;
+    text-align: center;
+    line-height: 44rpx;
+}
+
+.file-info {
+    flex: 1;
+    overflow: hidden;
+}
+
+.file-name {
+    font-size: 22rpx;
+    color: #A8A8A8;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+.delete-btn {
+    padding: 4rpx 8rpx;
+}
+
+.delete-icon {
+    font-size: 24rpx;
+    color: #C7C7C7;
+    line-height: 1;
+}
+
+.delete-icon:active {
+    color: #ff4444;
+}
+</style> 

+ 77 - 0
uni_modules/zhouquan-fileSelector/package.json

@@ -0,0 +1,77 @@
+{
+  "id": "zhouquan-fileSelector",
+  "name": "文件选择器",
+  "displayName": "文件选择器(APP、H5)",
+  "version": "1.0.1",
+  "description": "一个简单易用的文件选择器组件,支持多文件选择、文件类型识别、文件列表展示等功能。支持H5和APP平台",
+  "keywords": [
+    "文件",
+    "文件选择",
+    "多文件",
+    "文件列表"
+],
+  "repository": "",
+"engines": {
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "插件不采集任何数据",
+      "permissions": "无"
+    },
+    "npmurl": "",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "n"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "n",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "n",
+          "阿里": "n",
+          "百度": "n",
+          "字节跳动": "n",
+          "QQ": "n"
+        },
+        "快应用": {
+          "华为": "n",
+          "联盟": "n"
+        }
+      }
+    }
+  }
+}