This commit is contained in:
jiangrui 2021-04-19 10:22:26 +08:00
parent 73ca8c141d
commit bd429c8c08
13 changed files with 1407 additions and 52 deletions

View File

@ -4,3 +4,4 @@ ENV = 'development'
# base api
#VUE_APP_BASE_API = '/dev-api'
VUE_APP_BASE_API = 'http://app.rt.xianci.info/'
#VUE_APP_BASE_API = 'http://api.rt.myntv.cn/'

View File

@ -37,6 +37,7 @@
"tui-editor": "1.3.3",
"vue": "2.6.10",
"vue-count-to": "1.0.13",
"vue-cropper": "^0.5.3",
"vue-router": "3.0.2",
"vue-splitpane": "1.0.4",
"vuedraggable": "2.20.0",

13
src/api/region.js Normal file
View File

@ -0,0 +1,13 @@
import request from "@/utils/request";
/**
* 行政区域
* @param {*} data
* @returns
*/
export function getRegions() {
return request({
url: "/api/convenience/residentInformation/region/list",
method: "get"
});
}

View File

@ -0,0 +1,29 @@
import request from "@/utils/request";
/**
* 居民信息列表
* @param {*} data
* @returns
*/
export function getResidentInfoList(data) {
return request({
url: "/api/convenience/residentInformation/list",
method: "post",
data
});
}
/**
* 添加居民信息
* @param {*} data
* @returns
*/
export function addResidentInfoList(data) {
return request({
url: "/api/convenience/residentInformation/add",
method: "post",
data
});
}

View File

@ -0,0 +1,67 @@
<template>
<el-cascader
v-model="regionNames"
style="width:240px"
:options="regions"
:props="{ checkStrictly: true }"
placeholder=""
@change="emitSelectValue"
clearable
/>
</template>
<script>
import { getRegions } from "@/api/region";
export default {
data() {
return {
regionNames: [],
regions: []
};
},
// props:['organizationAttribution','permanentResidenceRegion','permanentResidenceTown'],
props: ["_regionNames"],
created() {
this.getList();
// console.log(233);
this.regionNames = this._regionNames;
},
methods: {
getList() {
getRegions().then(res => {
const tempData = res.d;
const countyArr = []; // ,1
tempData.map(county => {
const countyObj = {
value: county.root.name,
label: county.root.name,
disabled: true,
children: []
};
countyArr.push(countyObj);
county.node.map(town => {
const townObj = {
value: town.root.name,
label: town.root.name,
children: []
};
countyObj.children.push(townObj);
town.node.map(country => {
townObj.children.push({
value: country.root.name,
label: country.root.name
});
});
});
});
this.regions = countyArr;
});
},
emitSelectValue(val) {
this.$emit("input", val);
}
}
};
</script>
<style></style>

View File

@ -0,0 +1,273 @@
<template>
<div>
<!-- 多图片上传 -->
<el-upload v-if="multiple" action="string" list-type="picture-card" :on-preview="handlePreview" :auto-upload="false" :on-remove="handleRemove" :http-request="upload" :on-change="consoleFL" :file-list="uploadList">
<i class="el-icon-plus" />
</el-upload>
<!-- 单图片上传 -->
<el-upload v-else class="avatar-uploader" action="'string'" :auto-upload="false" :show-file-list="false" :on-change="handleCrop" :http-request="upload">
<img v-if="imageUrl" ref="singleImg" :src="imageUrl" class="avatar" :style="{width:width+'px',height:height+'px'}" @mouseenter="mouseEnter" @mouseleave="mouseLeave">
<i v-else class="el-icon-plus avatar-uploader-icon" :style="{width:width+'px',height:height+'px','line-height':height+'px','font-size':height/6+'px'}" />
<!-- 单图片上传状态显示 -->
<!-- <div v-if="imageUrl" class="reupload" ref="reupload" @click.stop="handlePreviewSingle" @mouseenter="mouseEnter" @mouseleave="mouseLeave" :style="{width:reuploadWidth+'px',height:reuploadWidth+'px','line-height':reuploadWidth+'px','font-size':reuploadWidth/5+'px'}">重新上传</div> -->
<div v-if="imageUrl" id="uploadIcon" ref="reupload" :style="{width:'100%'}" @mouseenter="mouseEnter" @mouseleave="mouseLeave">
<i class="el-icon-zoom-in" :style="{color:'#2E2E2E',fontSize:'25px',display:'inline-block',paddingRight:'15px'}" @click.stop="handlePreviewSingle" />
<!-- <i class="el-icon-upload" :style="{color:'#2E2E2E',fontSize:'25px',display:'inline-block'}" /> -->
<i class="el-icon-delete" :style="{color:'#2E2E2E',fontSize:'25px',display:'inline-block'}" @click.stop="handlePicRemove" />
</div>
<div ref="uploading" class="reupload" :style="{width:reuploadWidth+'px',height:reuploadWidth+'px','line-height':reuploadWidth+'px','font-size':reuploadWidth/5+'px'}">上传中..</div>
<div ref="failUpload" class="reupload" :style="{width:reuploadWidth+'px',height:reuploadWidth+'px','line-height':reuploadWidth+'px','font-size':reuploadWidth/5+'px'}">上传失败</div>
</el-upload>
<!-- 多图片预览弹窗 -->
<el-dialog :visible.sync="dialogVisible" append-to-body>
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
<!-- 剪裁组件弹窗 -->
<el-dialog :visible.sync="cropperModel" :width="news ? '990px' : '800px'" :before-close="beforeClose" append-to-body>
<Cropper ref="vueCropper" :img-file="file" :fixed-number="fixedNumber" :news="news" @upload="upload" />
</el-dialog>
</div>
</template>
<script>
import Cropper from '@/components/uploader/cropper'
// import axios from 'axios'
import OSS from '@/utils/aliOSS'
import UUID from 'uuidjs'
export default {
name: 'Uploader',
components: {
Cropper
},
props: {
targetUrl: {
//
type: String,
default: 'http://120.77.82.246:6789/api/postUpLoadFiles'
},
multiple: {
//
type: Boolean,
default: false
},
initUrl: {
//
default: ''
},
fixedNumber: {
//
default: function() {
return [3, 1]
}
},
news: {
type: Boolean,
default: false
},
width: {
//
type: Number,
default: 178
},
height: {
//
type: Number,
default: 178
}
},
data() {
return {
file: '', //
imageUrl: '', //
dialogImageUrl: '', //
uploadList: [], //
reupload: true, // ""
dialogVisible: false, //
cropperModel: false, //
reuploadWidth: this.height * 0.7 //
}
},
watch: {
initUrl: function(val) {
//
console.info('watch')
console.info(val)
if (val) {
if (typeof this.initUrl === 'string') {
this.imageUrl = val
} else {
this.uploadList = this.formatImgArr(val)
}
} else {
this.imageUrl = ''
this.uploadList = []
}
}
},
updated() {
if (this.$refs.vueCropper) {
this.$refs.vueCropper.Update()
}
},
mounted() {
if (typeof this.initUrl === 'string') {
this.imageUrl = this.initUrl
} else {
this.uploadList = this.formatImgArr(this.initUrl)
}
},
methods: {
/** **************************** multiple多图情况 **************************************/
handlePreview(file) {
//
this.dialogImageUrl = file.url
this.dialogVisible = true
},
handleRemove(file, fileList) {
//
this.uploadList = fileList
this.$emit('imgupload', this.formatImgArr(this.uploadList))
},
handlePicRemove(file) {
this.imageUrl = ''
this.$emit('imgupload', this.imageUrl)
},
consoleFL(file, fileList) {
//
this.cropperModel = true
this.file = file
this.uploadList = fileList
},
/** **********************************************************************************/
/** **************************** single单图情况 **************************************/
handlePreviewSingle(file) { //
// this.dialogImageUrl = this.file.url
this.dialogImageUrl = this.imageUrl
this.dialogVisible = true
},
mouseEnter() { //
this.$refs.reupload.style.display = 'block'
if (this.$refs.failUpload.style.display === 'block') {
this.$refs.failUpload.style.display = 'none'
}
this.$refs.singleImg.style.opacity = '0.6'
},
mouseLeave() {
//
this.$refs.reupload.style.display = 'none'
this.$refs.singleImg.style.opacity = '1'
},
handleCrop(file, files) {
file.url = URL.createObjectURL(file.raw)
// console.log(file)
//
this.cropperModel = true
this.file = file
// this.imageUrl = file.url
},
/** **********************************************************************************/
upload(data) {
// upload
if (!this.multiple) {
//
this.$refs.uploading.style.display = 'block'
}
// const formData = new FormData()
// formData.append('file', data, '123.png')
// axios.defaults.withCredentials = false
// axios.post(this.targetUrl, formData, { headers: { 'Content-Type': 'multipart/form-data' }}).then(res => {
const name = UUID.generate() + '.png'
OSS(data, name).then(res => {
if (!this.multiple) {
//
this.$refs.uploading.style.display = 'none'
}
// console.log(res)
if (res.res.status === 200) {
//
const currentPic = res.url
if (this.multiple) {
this.uploadList.pop()
this.uploadList.push({
url: currentPic,
uid: '111'
})
// console.log(this.formatImgArr(this.uploadList))
this.$emit('imgupload', this.formatImgArr(this.uploadList))
} else {
this.$emit('imgupload', currentPic)
}
} else {
this.$message({
type: 'warning',
message: '上传失败!'
})
//
if (!this.multiple) {
this.$refs.failUpload.style.display = 'block'
} else {
this.uploadList.pop()
}
}
})
this.cropperModel = false
},
formatImgArr(arr) {
const result = arr.map((item, index) => {
if (typeof item === 'string') {
return {
url: item,
uid: `index${index}`
}
} else {
return item.url
}
})
return result
},
beforeClose(done) {
this.uploadList.pop()
this.cropperModel = false
}
}
}
</script>
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
color: #8c939d;
text-align: center;
}
.avatar {
display: block;
}
.reupload {
border-radius: 50%;
position: absolute;
color: #fff;
background-color: #000000;
opacity: 0.6;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: none;
}
#uploadIcon{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: none;
}
</style>

View File

@ -0,0 +1,596 @@
<template>
<div>
<div v-if="news" class="font-box">
<span />
<span style="margin-right: 70px">热门预览</span>
<span style="margin-right: 6px">非热门预览</span>
</div>
<div class="cropper-content">
<!-- 剪裁框 -->
<div class="cropper">
<vueCropper
ref="cropper"
:img="option.img"
:output-size="option.size"
:output-type="option.outputType"
:info="true"
:full="option.full"
:can-move="option.canMove"
:can-move-box="option.canMoveBox"
:original="option.original"
:auto-crop="option.autoCrop"
:auto-crop-width="option.autoCropWidth"
:auto-crop-height="option.autoCropHeight"
:fixed-box="option.fixedBox"
:fixed="option.fixed"
:fixed-number="fixedNumber"
@realTime="realTime"
/>
</div>
<!-- 预览框 -->
<div class="show-preview" :style="{'width': '300px', 'height': '300px', 'overflow': 'hidden', 'margin': '0 25px', 'display':'flex', 'align-items' : 'center'}" @mousedown="previewContainerDown" @mousemove="previewContainerMove" @mouseup="previewContainerUp" @mouseleave="previewContainerLeave">
<div :style="previews.div" style="position: relative">
<div :ref="previewId" :style="previews.div" class="preview">
<img :src="previews.url" :style="previews.img">
<img v-if="water.url" :src="water.url" :style="water.img" class="water">
</div>
<div v-show="water.url" :style="previews.div" class="water-cropper">
<!-- @mousemove.self="cropMove" -->
<div class="crop-box" :style="water.img" @mousedown.self="cropDown">
<span class="crop-line line-w" />
<span class="crop-line line-a" />
<span class="crop-line line-s" />
<span class="crop-line line-d" />
<span class="crop-point point1" @mousedown.stop="pointDown($event, 'lt')" />
<span class="crop-point point2" @mousedown.stop="pointDown($event, 'rt')" />
<span class="crop-point point3" @mousedown.stop="pointDown($event, 'lb')" />
<span class="crop-point point4" @mousedown.stop="pointDown($event, 'rb')" />
</div>
</div>
</div>
</div>
<!-- 新闻预览框 -->
<div v-if="news" class="news-preview" :style="{'width': '200px', 'height': '300px', 'overflow': 'hidden', 'margin': '0', 'display':'flex', 'align-items' : 'center'}">
<!-- <div class="news-box"> -->
<div :ref="previewId" :style="previews.div" class="preview">
<img :src="previews.url" :style="previews.img" style="margin-left: -75px">
<img v-if="water.url" :src="water.url" :style="water.img" style="margin-left: -75px" class="water">
</div>
<!-- </div> -->
</div>
</div>
<div class="footer-btn">
<!-- 缩放旋转按钮 -->
<div class="scope-btn">
<el-button type="primary" icon="el-icon-zoom-in" @click="changeScale(1)" />
<el-button type="primary" icon="el-icon-zoom-out" @click="changeScale(-1)" />
<el-button type="primary" @click="rotateLeft">逆时针旋转</el-button>
<el-button type="primary" @click="rotateRight">顺时针旋转</el-button>
</div>
<!-- 确认上传按钮 -->
<div class="upload-btn">
<el-button type="primary" @click="uploadImg('blob')">上传</el-button>
<el-button type="primary" @click="selectWater">选择水印</el-button>
<el-button type="primary" @click="water.url = null">取消水印</el-button>
<!-- <input type="file" name="" id="" accept=""> -->
</div>
</div>
</div>
</template>
<script>
// import VueCropper from 'vue-cropper'
// import html2canvas from 'html2canvas'
// import { createElement } from 'dropzone'
export default {
props: ['imgFile', 'fixedNumber', 'news'],
data() {
name:'Cropper'
return {
previewId: 'waterProview' + Date.parse(new Date()),
previews: {}, //
option: {
img: '', // ()
size: 1, // (:1)
full: true, // true (:false)
outputType: 'png', // (:jpg)
canMove: true, // (:true)
original: false, // (:false)
canMoveBox: true, // (:true)
autoCrop: true, // (:false)
autoCropWidth: 400, // (:80%)
autoCropHeight: 400, // (:80%)
fixedBox: false, // (:false)
fixed: true, // (:true)
fixedNumber: [1.5, 1] // (:[1:1])
},
downImg: '#',
//
waterUrl: null,
water: {
url: null,
img: {
width: '100px',
height: '100px',
left: '0px',
top: '0px'
}
},
waterBox: {
left: 0,
top: 0,
width: 100,
height: 100,
scale: 1,
prop: 1
},
waterInfo: {
waterX: 0,
waterY: 0,
x: 0,
y: 0
},
waterMove: false,
pointInfo: {
pointX: 0,
pointY: 0,
x: 0,
y: 0,
width: 0,
height: 0
},
pointType: null,
pointIsMove: false,
successimg: null
}
},
watch: {
waterBox: {
handler(newValue, oldValue) {
// console.log(newValue)
this.water.img.left = newValue.left + 'px'
this.water.img.top = newValue.top + 'px'
this.water.img.width = newValue.width + 'px'
this.water.img.height = newValue.height + 'px'
},
deep: true
}
},
methods: {
changeScale(num) {
//
num = num || 1
this.$refs.cropper.changeScale(num)
},
rotateLeft() {
//
console.log(this.$refs.cropper)
this.$refs.cropper.rotateLeft()
},
rotateRight() {
//
console.log(this.$refs.cropper)
this.$refs.cropper.rotateRight()
},
Update() {
// this.file = this.imgFile
this.option.img = this.imgFile.url
},
realTime(data) {
//
this.previews = data
},
uploadImg(type) {
//
event.preventDefault()
const that = this
this.$refs.cropper.getCropData(async data => {
// that.$emit('upload', data)
let cropInfo, waterInfo
if (!this.water.url) {
if (type === 'blob') {
this.$refs.cropper.getCropBlob(data => {
that.$emit('upload', data)
})
} else {
this.$refs.cropper.getCropData(data => {
that.$emit('upload', data)
})
}
}
await (() => {
const cropImg = new Image()
cropImg.src = data
cropImg.onload = () => {
cropInfo = {
img: cropImg,
width: cropImg.width,
height: cropImg.height,
x: 0,
y: 0
}
const waterImg = new Image()
waterImg.crossOrigin = ''
waterImg.src = this.water.url
waterImg.onload = () => {
waterInfo = {
img: waterImg,
width: this.waterBox.width,
height: this.waterBox.height,
x: this.waterBox.left,
y: this.waterBox.top
}
// console.log(cropInfo)
// console.log(waterInfo)
const canvas = document.createElement('canvas')
canvas.width = cropInfo.width
canvas.height = cropInfo.height
const ctx = canvas.getContext('2d')
ctx.drawImage(cropInfo.img, cropInfo.x, cropInfo.y, cropInfo.width, cropInfo.height)
// console.log(this.previews.div)
const prop = cropInfo.width / parseFloat(this.previews.div.width)
ctx.drawImage(waterInfo.img, waterInfo.x * prop, waterInfo.y * prop, waterInfo.width * prop, waterInfo.height * prop)
// console.log(this.previews)
if (type === 'blob') {
canvas.toBlob((blob) => {
that.$emit('upload', blob)
})
} else {
that.$emit('upload', canvas.toDataURL('image/png'))
}
/*
// canvas
const canvas = document.createElement('canvas')
// DOM
const canvasDom = this.$refs[this.previewId]
//
const width = parseInt(window.getComputedStyle(canvasDom).width)
const height = parseInt(window.getComputedStyle(canvasDom).height)
// 2
canvas.width = width * 2
canvas.height = height * 2
canvas.style.width = width + 'px'
canvas.style.height = height + 'px'
const context = canvas.getContext('2d')
context.scale(2, 2)
const options = {
backgroundColor: null,
canvas: canvas,
useCORS: true
}
console.log(123)
html2canvas(canvasDom, options).then(canvas => {
// console.log(canvas.toDataURL('image/png'))
// that.successimg = canvas.toDataURL('image/png')
//
if (type === 'blob') {
console.log('blob')
canvas.toBlob((blob) => {
that.$emit('upload', blob)
})
} else {
that.$emit('upload', canvas.toDataURL('image/png'))
}
})
*/
}
}
})()
})
// return
//
// if (type === 'blob') {
// this.$refs.cropper.getCropBlob(data => {
// that.$emit('upload', data)
// })
// } else {
// this.$refs.cropper.getCropData(data => {
// that.$emit('upload', data)
// })
// }
},
//
selectWater() { //
const input = document.createElement('input')
input.type = 'file'
input.accept = 'image/*'
input.addEventListener('change', (e) => {
const file = e.path[0].files[0]
const imgURL = window.URL.createObjectURL(file)
const image = new Image()
image.src = imgURL
image.onload = () => {
this.water.url = imgURL
const { width, height } = image
const prop = width / height
this.waterBox.prop = prop
this.waterBox.height = 100
this.waterBox.width = 100 * prop
}
})
setTimeout(() => {
input.click()
}, 100)
},
previewContainerDown(e) {
},
previewContainerMove(e) {
if (this.waterMove) {
this.cropMove(e)
} else if (this.pointIsMove) {
this.pointMove(e)
}
},
previewContainerUp() {
this.waterMove = false
this.pointIsMove = false
},
previewContainerLeave() {
this.waterMove = false
this.pointIsMove = false
},
cropDown(e) {
const { clientX, clientY } = e
this.waterInfo.x = this.waterBox.left
this.waterInfo.y = this.waterBox.top
this.waterInfo.waterX = clientX
this.waterInfo.waterY = clientY
this.waterMove = true
},
cropMove(e) {
if (this.waterMove) {
const { clientX, clientY } = e
this.waterBox.left = (clientX - this.waterInfo.waterX) + this.waterInfo.x
this.waterBox.top = (clientY - this.waterInfo.waterY) + this.waterInfo.y
}
},
//
pointDown(e, type) {
const { clientX, clientY } = e
this.pointInfo.x = this.waterBox.left
this.pointInfo.y = this.waterBox.top
this.pointInfo.width = this.waterBox.width
this.pointInfo.height = this.waterBox.height
this.pointInfo.pointX = clientX
this.pointInfo.pointY = clientY
this.pointType = type
this.pointIsMove = true
},
pointMove(e) {
if (this.pointIsMove) {
const { clientX, clientY } = e
const left = (clientX - this.pointInfo.pointX) + this.pointInfo.x
const top = (clientY - this.pointInfo.pointY) + this.pointInfo.y
if (this.pointType === 'lt') {
this.waterBox.left = left
this.waterBox.top = top
const height = this.pointInfo.height - (clientY - this.pointInfo.pointY)
this.waterBox.height = height
this.waterBox.width = height * this.waterBox.prop
// this.waterBox.width = this.pointInfo.width - (clientX - this.pointInfo.pointX)
// this.waterBox.height = this.pointInfo.height - (clientY - this.pointInfo.pointY)
} else if (this.pointType === 'rt') {
this.waterBox.top = top
const height = this.pointInfo.height - (clientY - this.pointInfo.pointY)
this.waterBox.height = height
this.waterBox.width = height * this.waterBox.prop
// this.waterBox.width = this.pointInfo.width + (clientX - this.pointInfo.pointX)
// this.waterBox.height = this.pointInfo.height - (clientY - this.pointInfo.pointY)
} else if (this.pointType === 'lb') {
this.waterBox.left = left
const height = this.pointInfo.height + (clientY - this.pointInfo.pointY)
this.waterBox.height = height
this.waterBox.width = height * this.waterBox.prop
// this.waterBox.width = this.pointInfo.width - (clientX - this.pointInfo.pointX)
// this.waterBox.height = this.pointInfo.height + (clientY - this.pointInfo.pointY)
} else if (this.pointType === 'rb') {
const height = this.pointInfo.height + (clientY - this.pointInfo.pointY)
this.waterBox.height = height
this.waterBox.width = height * this.waterBox.prop
// this.waterBox.width = this.pointInfo.width + (clientX - this.pointInfo.pointX)
// this.waterBox.height = this.pointInfo.height + (clientY - this.pointInfo.pointY)
}
}
}
}
// components: { VueCropper }
}
</script>
<style>
.cropper-content {
display: flex;
display: -webkit-flex;
justify-content: flex-end;
-webkit-justify-content: flex-end;
}
.cropper-content .cropper {
width: 350px;
height: 300px;
}
.cropper-content .show-preview {
flex: 1;
-webkit-flex: 1;
display: flex;
display: -webkit-flex;
justify-content: center;
-webkit-justify-content: center;
overflow: hidden;
border: 1px solid #cccccc;
background: #cccccc;
margin-left: 40px;
}
.preview {
overflow: hidden;
border: 1px solid #cccccc;
background: #cccccc;
}
.footer-btn {
margin-top: 30px;
display: flex;
display: -webkit-flex;
justify-content: flex-end;
-webkit-justify-content: flex-end;
}
.footer-btn .scope-btn {
width: 380px;
display: flex;
display: -webkit-flex;
justify-content: space-between;
-webkit-justify-content: space-between;
}
.footer-btn .upload-btn {
flex: 1;
-webkit-flex: 1;
display: flex;
display: -webkit-flex;
justify-content: center;
-webkit-justify-content: center;
}
.footer-btn .btn {
outline: none;
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
-webkit-appearance: none;
text-align: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline: 0;
margin: 0;
-webkit-transition: 0.1s;
transition: 0.1s;
font-weight: 500;
padding: 8px 15px;
font-size: 12px;
border-radius: 3px;
color: #fff;
background-color: #67c23a;
border-color: #67c23a;
}
/* 水印 */
.cropper-content .show-preview{
position: relative;
user-select: none;
}
.water-cropper{
position: absolute;
top: 0;
right: 0;
/* display: flex;
align-items: center; */
}
.water{
position: absolute;
top: 0;
right: 0;
}
.preview {
position: relative;
}
.crop-box{
/* position: relative; */
position: absolute;
width: 50px;
height: 50px;
cursor: move;
}
.crop-line, .crop-point{
background: #39f;
position: absolute;
user-select: none
}
.crop-line{
opacity: 0.5;
}
.line-w, .line-s{
height: 1px;
width: 100%;
}
.line-a, .line-d{
width: 1px;
height: 100%;
}
.line-w{
top: 0;
left: 0;
}
.line-s{
top: 100%;
left: 0;
}
.line-a{
top: 0;
left: 0;
}
.line-d{
top: 0;
left: 100%;
}
.crop-point{
width: 8px;
height: 8px;
opacity: 0.75;
background-color: #39f;
border-radius: 100%;
}
.water-cropper .point1{
top: -4px;
left: -4px;
cursor: nw-resize;
}
.water-cropper .point2{
top: -4px;
right: -4px;
cursor: ne-resize;
}
.water-cropper .point3{
left: -4px;
bottom: -4px;
cursor: sw-resize;
}
.water-cropper .point4{
right: -4px;
bottom: -4px;
cursor: se-resize;
}
/* #39f */
.news-preview{
border: 1px solid #cccccc;
background: #cccccc;
}
/* .show-preview.news{
display: flex;
align-items: center;
} */
/* .news .news-box{
overflow: hidden;
} */
.font-box{
display: flex;
justify-content: space-around;
padding-bottom: 10px;
}
</style>

View File

@ -4,6 +4,7 @@ import Cookies from 'js-cookie'
import 'normalize.css/normalize.css' // a modern alternative to CSS resets
import Element from 'element-ui'
import './styles/element-variables.scss'
import enLang from 'element-ui/lib/locale/lang/en'// 如果使用中文语言包请默认支持,无需额外引入,请删除该依赖
@ -14,6 +15,9 @@ import App from './App'
import store from './store'
import router from './router'
import VueCropper from 'vue-cropper'
Vue.use(VueCropper)
import './icons' // icon
import './permission' // permission control
import './utils/error-log' // error log

View File

@ -157,6 +157,30 @@ export const constantRoutes = [
}
}
]
},
{
path: "/residentInformation",
component: Layout,
name: "ResidentInformation",
redirect: "/residentInformation/list",
meta: {
title: "居民信息管理",
icon: "list",
roles: ["admin", "editor"]
},
children: [
{
path: "list",
component: () => import("@/views/residentInformation/list"),
name: "residentInformationList",
meta: {
title: "居民信息列表",
icon: "list",
affix: true,
parentTitle: "居民信息管理"
}
}
]
}
// {
// path: "/staff",

View File

@ -1,9 +1,6 @@
<template>
<div class="app-container">
<div class="filter-container">
<!-- abc -->
</div>
<!-- bbc -->
<div class="filter-container"></div>
<el-form
ref="convenienceForm"
style="width:680px"
@ -19,7 +16,7 @@
/>
</el-form-item>
<el-form-item label="办事项目">
<el-form-item label="办事项目" prop="eventTypeId">
<!-- <el-cascader
v-model="convenience.eventTypeId"
:options="typeOptions"
@ -40,7 +37,7 @@
</el-select>
</el-form-item>
<el-form-item label="文字说明">
<el-form-item label="文字说明" prop="remark">
<el-input
v-model="convenience.remark"
type="textarea"
@ -49,7 +46,7 @@
/>
</el-form-item>
<el-form-item label="附件">
<el-form-item label="附件" prop="attachment">
<AliOss v-model="convenience.attachment" multiple />
</el-form-item>
<el-form-item>
@ -72,7 +69,6 @@ export default {
var checkInfo = (rule, value, callback) => {
if (value === "") {
callback(new Error("受理人不能为空"));
// console.log('value==<>');
} else {
info({ param: this.convenience.residentInformation || undefined }).then(
res => {
@ -81,7 +77,6 @@ export default {
} else {
this.searchInput = res.d.id;
callback();
// this.convenience.residentInformation = res.d.id;
}
}
);
@ -93,11 +88,16 @@ export default {
eventTypeId: undefined,
remark: undefined,
sponsor: "STAFF",
attachment: undefined
attachment:undefined
},
typeOptions: [],
rules: {
residentInformation: [{ validator: checkInfo, trigger: "blur" }]
residentInformation: [{ validator: checkInfo, trigger: "blur" }],
eventTypeId: [
{ required: true, message: "办事项目不能为空", trigger: "change" }
]
// remark: [{ validator: checkRemark, trigger: "blur" }],
// attachment: [{ validator: checkAttachment, trigger: "change" }]
},
fileList: undefined,
searchInput: undefined
@ -109,32 +109,22 @@ export default {
methods: {
getTypeList() {
typeList().then(res => {
// this.typeOptions = [];
this.typeOptions = res.d
// res.d.map(i => {
// const children = [];
// i.eventTypesList.map(c => {
// const level2 = {
// label: c.eventName,
// value: c.id
// };
// children.push(level2);
// });
// const level1 = {
// label: i.eventName,
// children
// };
// this.typeOptions.push(level1);
// });
this.typeOptions = res.d;
});
},
handleAdd() {
this.$refs.convenienceForm.validate(valid => {
if (valid) {
this.convenience.residentInformation = this.searchInput;
add(this.convenience).then(res => {
if (!this.convenience.remark) {
if (!(this.convenience.attachment&&this.convenience.attachment.length > 0)) {
this.$notify.error("文字说明与附件必须含有一项");
return;
}
}
let payload = Object.assign({}, this.convenience);
payload.residentInformation = this.searchInput;
add(payload).then(res => {
if (res.c === 200) {
this.$notify.success("创建成功");
this.$router.push("/convenience/list");

View File

@ -50,7 +50,9 @@
type="text"
style="font-size:16px"
@click="cascader"
><i class="icon_edit"/>{{ isEventTypeChange ? "取消" : "编辑" }}</el-button
><i class="icon_edit" />{{
isEventTypeChange ? "取消" : "编辑"
}}</el-button
>
</div>
<div style="color:#787878;font-size:14px;">
@ -456,11 +458,40 @@
<h3>{{ item.content }}</h3>
<p v-if="item.type === 11">审批人{{ getNames(item.to) }}</p>
<p
v-for="(attachment, index) in item.attachmentList"
:key="index"
<p v-for="(file, index) in item.attachmentList" :key="index">
<i
v-if="convenience.residentInformation.id === item.from.id"
class="icon_fujian_self"
/><i v-else class="icon_fujian" />
<!-- file -->
<span
v-if="
isAssetTypeAnAudio(file.substr(file.lastIndexOf('.') + 1))
"
@click="handleSetAudio(file)"
>{{ file }}</span
>
<span
v-if="
isAssetTypeAnImage(
file.substr(file.lastIndexOf('.') + 1)
)
"
@click="handleSetPreviewImg(file)"
>{{file}}</span>
<el-link
v-else
:href="file"
:underline="false"
target="_blank"
>
{{ file.substring(file.lastIndexOf("/") + 1) }}
</el-link>
<!-- <span
v-if="
isAssetTypeAnImage(
attachment.substr(attachment.lastIndexOf('.') + 1)
@ -473,19 +504,31 @@
v-if="convenience.residentInformation.id === item.from.id"
class="icon_fujian_self"
/><i v-else class="icon_fujian" />
{{
attachment.substring(attachment.lastIndexOf("/") + 1)
}}</span
{{ attachment.substring(attachment.lastIndexOf("/") + 1) }}
</span>
<span
v-else-if="
isAssetTypeAnAudio(
attachment.substr(attachment.lastIndexOf('.') + 1)
)
"
@click="handleSetAudio(attachment)"
>
</span>
<el-link
v-else
:href="attachment"
:underline="false"
target="_blank"
>
<i class="icon_fujian" />
<i
v-if="convenience.residentInformation.id === item.from.id"
class="icon_fujian_self"
/><i v-else class="icon_fujian" />
{{ attachment.substring(attachment.lastIndexOf("/") + 1) }}
</el-link>
</el-link> -->
</p>
<p v-if="item.type === 13 || item.type === 10">
审批结果{{ item.approve ? "通过" : "驳回" }}
@ -532,10 +575,31 @@
</div>
</div>
<el-dialog :visible.sync="dialogPreviewImg" width="500px">
<el-dialog
:visible.sync="dialogPreviewImg"
width="500px"
title="预览"
:destroy-on-clos="true"
>
<img width="100%" :src="dialogImageUrl" alt="" />
</el-dialog>
<el-dialog
:visible.sync="dialogAudio"
width="500px"
title="预览"
:destroy-on-clos="true"
@close="handleClose"
>
<video
id="myVideo"
:src="dialogAudioSrc"
controls
autoplay
width="100%"
/>
</el-dialog>
<el-dialog
class="dialog"
center
@ -827,13 +891,19 @@ export default {
},
dialogPreviewImg: false,
dialogImageUrl: undefined,
dialogAudio: false,
dialogAudioSrc: undefined,
dialogApprovalReminder: false,
dialogEventType: false,
typeOptions: [],
eventTypeId: undefined,
applyDetail: false,
isEventTypeChange: false,
cxPop: false
cxPop: false,
audioSources: [
"http://storage.myntv.cn/files20210415_142012.m4a"
// 'https://img.tukuppt.com/newpreview_music/08/99/49/5c897788e421b53181.mp3'
]
};
},
created() {
@ -1158,7 +1228,7 @@ export default {
case 16:
//
// this.applyDialog = true;
this.$notify.warning('功能尚未开发完成,敬请期待')
this.$notify.warning("功能尚未开发完成,敬请期待");
break;
default:
this.updateData();
@ -1255,6 +1325,18 @@ export default {
this.dialogImageUrl = imgurl;
this.dialogPreviewImg = true;
},
isAssetTypeAnAudio(ext) {
// console.log(e);
return ["m4a", "mp4", "mp3", "mov"].indexOf(ext.toLowerCase()) !== -1;
},
handleSetAudio(src) {
this.dialogAudio = true;
this.dialogAudioSrc = src;
},
handleClose() {
// console.log('close');
document.getElementById("myVideo").pause();
},
handleChangeEventType() {
this.dialogEventType = true;
},
@ -1275,8 +1357,7 @@ export default {
this.$nextTick(() => {
const tempRef = this.$refs.eventTypeRef;
console.log(tempRef);
if(tempRef)
tempRef.toggleMenu();
if (tempRef) tempRef.toggleMenu();
// tempRef.$el.style.display = "inline-block";
// this.$refs.eventTypeRef.focusFirstNode();
}, 100);
@ -1542,7 +1623,7 @@ h3 {
margin-right: 6px;
vertical-align: top;
}
.icon_edit{
.icon_edit {
background: url("./img/icon_edit.png") no-repeat;
width: 16px;
height: 16px;

View File

@ -120,8 +120,8 @@ export default {
// password: '111111'
// },
loginForm: {
username: '',
captcha: '',
username: '22222222222',
captcha: '111111',
appVersion: '1.0.0',
system: 'IOS',
device: navigator.userAgent,
@ -339,7 +339,7 @@ button:focus {
padding: 12px 5px 12px 15px;
color: $light_gray;
height: 47px;
caret-color: $cursor;
caret-color: #000;
&:-webkit-autofill {
box-shadow: 0 0 0px 1000px $bg inset !important;

View File

@ -0,0 +1,276 @@
<template>
<div class="app-container" style="padding-top:0">
<!-- <div>{{ list }}</div> -->
<div class="filter-container">
<el-input
v-model="listQuery.params.name"
style="width:200px;"
class="filter-item"
placeholder="姓名"
clearable
/>
<el-input
v-model="listQuery.params.phone"
style="width:200px;"
class="filter-item"
placeholder="手机号"
clearable
/>
<el-input
v-model="listQuery.params.idCard"
style="width:200px;"
class="filter-item"
placeholder="身份证号"
clearable
/>
<RegionSelect v-model="regionSearchVal" />
<el-button type="primary" @click="handleSearch">搜索</el-button>
</div>
<el-button
@click="handleAdd"
type="primary"
size="mini"
style="font-size:12px;padding:10px 20px"
>新增</el-button
>
<el-table :data="list" style="width: 100%" border>
<el-table-column prop="name" label="姓名" width="180">
<template slot-scope="scope">
{{ scope.row.name }}
</template>
</el-table-column>
<el-table-column prop="nameZ" label="姓名(藏语)" width="180">
<template slot-scope="scope">
{{ scope.row.nameZ }}
</template>
</el-table-column>
<el-table-column prop="phone" label="手机号">
<template slot-scope="scope">
{{ scope.row.phone }}
</template>
</el-table-column>
<el-table-column prop="phone" label="身份证号">
<template slot-scope="scope">
{{ scope.row.idCard }}
</template>
</el-table-column>
<el-table-column prop="phone" label="所属行政区域">
<template slot-scope="scope">
{{
scope.row.permanentResidenceRegion +
"-" +
scope.row.permanentResidenceTown +
"-" +
scope.row.permanentResidenceTownShip
}}
</template>
</el-table-column>
<el-table-column label="操作" width="300px" fixed="right" align="center">
<template slot-scope="scope">
<el-button
@click="handleEdit(scope.row)"
type="primary"
size="mini"
style="font-size:12px;padding:10px 20px"
>修改</el-button
>
</template>
</el-table-column>
</el-table>
<el-dialog title="提示" :visible.sync="dialogAddVisible" width="30%">
<!-- <span></span> -->
<!-- <div>居民名称</div> -->
<el-form
:model="residentInfo"
ref="residentInfoForm"
label-width="155px"
class="el-form"
:rules="rules"
>
<el-form-item label="居民头像">
<cut-upload
:fixed-number="[1, 1]"
:init-url="residentInfo.avatar"
@imgupload="UploadCbk"
/>
</el-form-item>
<el-form-item label="居民姓名">
<el-input v-model="residentInfo.name" />
</el-form-item>
<el-form-item label="居民姓名(藏文)">
<el-input v-model="residentInfo.nameZ" />
</el-form-item>
<el-form-item label="性别">
<el-radio v-model="residentInfo.sex" label="0"></el-radio>
<el-radio v-model="residentInfo.sex" label="1"></el-radio>
<el-radio v-model="residentInfo.sex" label="2">未知</el-radio>
</el-form-item>
<el-form-item label="居民姓名手机号">
<el-input v-model="residentInfo.phone" />
</el-form-item>
<el-form-item label="居民姓名身份证号">
<el-input v-model="residentInfo.idCard" />
</el-form-item>
<el-form-item label="描述">
<el-input v-model="residentInfo.description" />
</el-form-item>
<el-form-item label="描述(藏文)">
<el-input v-model="residentInfo.descriptionZ" />
</el-form-item>
<el-form-item label="是否是便民工作人员">
<el-switch v-model="residentInfo.idConvenience"></el-switch>
</el-form-item>
<el-form-item label="是否是便民领导">
<el-switch v-model="residentInfo.isConvenienceLeader"></el-switch>
</el-form-item>
<el-form-item label="是否是融媒体工作人员">
<el-switch v-model="residentInfo.isNews"></el-switch>
</el-form-item>
<el-form-item label="是否是融媒体领导">
<el-switch v-model="residentInfo.isNewsLeader"></el-switch>
</el-form-item>
<el-form-item label="行政区域">
<RegionSelect
v-model="residentInfo.regionIds"
:regionNames="[
residentInfo.permanentResidenceRegion,
residentInfo.permanentResidenceTown,
residentInfo.permanentResidenceTownShip
]"
/>
</el-form-item>
</el-form>
<!-- logconven.ne -->
<span slot="footer" class="dialog-footer">
<el-button @click="dialogAddVisible = false"> </el-button>
<el-button type="primary" @click="createData"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {
getResidentInfoList,
addResidentInfoList
} from "@/api/residentInformation";
import RegionSelect from "@/components/RegionSelect";
import { getRegions } from "@/api/region";
import Pagination from "@/components/Pagination";
import CutUpload from "@/components/cutUploadImage/index.vue";
export default {
components: { Pagination, CutUpload, RegionSelect },
data() {
return {
listQuery: {
lastPageIndex: 1,
size: 10,
params: {
name: "",
phone: "",
idCard: ""
}
},
dialogAddVisible: false,
list: [],
rules: {},
residentInfo: {
avatar: ""
},
regionSearchVal: undefined
};
},
created() {
this.getList();
// this.getRegions();
},
methods: {
getList() {
getResidentInfoList(this.listQuery).then(res => {
console.log(res);
this.list = res.d.records;
});
},
getRegions() {
getRegions().then(res => {
console.log(res);
});
},
handleSearch() {
if (this.regionSearchVal) {
this.listQuery.params.permanentResidenceRegion = this.regionSearchVal[0];
this.listQuery.params.permanentResidenceTown = this.regionSearchVal[1];
this.listQuery.params.permanentResidenceTownShip = this.regionSearchVal[2];
}
this.getList();
},
resetResidentInfo() {
this.residentInfo = {
id: undefined,
avatar: undefined,
name: undefined,
nameZ: undefined,
description: undefined,
descriptionZ: undefined,
duty: undefined,
dutyZ: undefined,
idCard: undefined,
idCardAddress: undefined,
isConvenience: false,
isConvenienceLeader: false,
isNews: false,
isNewsLeader: false,
isResidentsAdministrator: false,
phone: undefined,
sex: undefined,
regionIds: undefined
};
},
UploadCbk(data) {
this.residentInfo.avatar = data;
},
handleAdd() {
// this.resetResidentInfo();
this.$nextTick(() => {
this.dialogAddVisible = true;
});
},
formatSubmitParams() {
let regions = this.residentInfo.regionIds;
this.residentInfo.permanentResidenceRegion = regions[0];
this.residentInfo.permanentResidenceTown = regions[1];
this.residentInfo.permanentResidenceTownShip = regions[2];
delete this.residentInfo.regionIds;
},
createData() {
this.formatSubmitParams();
// console.log(this.residentInfo);
addResidentInfoList([this.residentInfo]).then(res => {
console.log(res);
});
},
handleEdit(row) {
this.residentInfo = Object.assign({}, row);
this.dialogAddVisible = true;
}
}
};
</script>
<style></style>