changes
This commit is contained in:
84
components/media/Camera.vue
Normal file
84
components/media/Camera.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div>
|
||||
<div><video ref="video" id="video" width="640" height="480" autoplay></video></div>
|
||||
<div class="mt-2">
|
||||
<button class="button is-primary" id="snap" v-on:click="capture()">Chụp ảnh</button>
|
||||
<a class="ml-6" @click="switchView()">
|
||||
<SvgIcon v-bind="{name: 'camera_switch.svg', type: 'black', size: 40}"></SvgIcon>
|
||||
</a>
|
||||
</div>
|
||||
<canvas v-show="false" ref="canvas" id="canvas" width="640" height="480"></canvas>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data: function() {
|
||||
return {
|
||||
video: {},
|
||||
canvas: {},
|
||||
current: 'front'
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.openCamera()
|
||||
},
|
||||
beforeDestroy() {
|
||||
var vidTrack = this.video.srcObject.getVideoTracks()
|
||||
vidTrack.forEach(track => {
|
||||
track.stop()
|
||||
track.enabled = false
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
openCamera() {
|
||||
let f = this.current==='front'? {facingMode: "user"} : {facingMode: {exact: "environment"}}
|
||||
this.video = this.$refs.video;
|
||||
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
||||
navigator.mediaDevices.getUserMedia({ video: f, audio: false }).then(stream => {
|
||||
video.srcObject = stream;
|
||||
this.video.play();
|
||||
});
|
||||
}
|
||||
},
|
||||
capture() {
|
||||
this.canvas = this.$refs.canvas;
|
||||
let scale = 600 / this.video.videoWidth;
|
||||
let w = this.video.videoWidth * scale;
|
||||
let h = this.video.videoHeight * scale;
|
||||
this.canvas.width = w;
|
||||
this.canvas.height = h;
|
||||
var context = this.canvas
|
||||
.getContext("2d")
|
||||
.drawImage(this.video,0,0,w,h);
|
||||
this.canvas.toBlob(blod=>this.saveAs(blod))
|
||||
},
|
||||
async saveAs(blod) {
|
||||
var form = new FormData();
|
||||
let name = `${this.$id()}.png`
|
||||
this.fileName = `${this.$dayjs(new Date()).format("YYYYMMDDhhmmss")}-${name}`
|
||||
form.append('filename', this.fileName)
|
||||
form.append('name', name)
|
||||
form.append('file', blod)
|
||||
form.append('type', 'image')
|
||||
form.append('size', 100)
|
||||
form.append('user', this.$store.state.login.id)
|
||||
let result = await this.$insertapi('upload', form)
|
||||
if(result==='error') return
|
||||
let row = result.rows[0]
|
||||
const file = new File([blod], name, {type: "image/png"})
|
||||
row.source = {file: file}
|
||||
this.$emit('modalevent', {name: 'selectimage', data: row})
|
||||
this.$emit('close')
|
||||
},
|
||||
switchView() {
|
||||
this.current = this.current==='front'? 'back' : 'front'
|
||||
var vidTrack = this.video.srcObject.getVideoTracks()
|
||||
vidTrack.forEach(track => {
|
||||
track.stop()
|
||||
track.enabled = false
|
||||
})
|
||||
this.openCamera()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
81
components/media/ChipImage.vue
Normal file
81
components/media/ChipImage.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="image-container">
|
||||
<a @click="open()"><img :src="image" style="max-height: 86vh !important;"></a>
|
||||
<div class="image-label">
|
||||
<span class="is-clickable" @click="copyImage()" v-if="display.findIndex(v=>v==='copy')>=0">
|
||||
<SvgIcon v-bind="{name: 'copy.svg', type: 'primary', size: 20}"></SvgIcon>
|
||||
</span>
|
||||
<span class="is-clickable ml-5" @click="download()" v-if="display.findIndex(v=>v==='download')>=0">
|
||||
<SvgIcon v-bind="{name: 'download.svg', type: 'primary', size: 20}"></SvgIcon>
|
||||
</span>
|
||||
<span class="is-clickable ml-5" @click="askConfirm()" v-if="display.findIndex(v=>v==='delete')>=0">
|
||||
<SvgIcon v-bind="{name: 'bin1.svg', type: 'danger', size: 18}"></SvgIcon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<Modal @close="showmodal=undefined" @remove="remove" @confirm="remove()" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { useStore } from '~/stores/index'
|
||||
export default {
|
||||
setup() {
|
||||
const store = useStore()
|
||||
return { store }
|
||||
},
|
||||
props: ['row', 'api', 'pagename', 'file', 'image', 'show', 'extend'],
|
||||
data() {
|
||||
return {
|
||||
showmodal: undefined,
|
||||
display: this.show || []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
if(!this.file || this.extend===false) return
|
||||
this.showmodal = {title: this.file.file__name, component: 'media/ChipImage',
|
||||
vbind: {extend: false, show: this.show, file: this.file,
|
||||
image: `${this.$getpath()}download/?name=${this.file.file__file || this.file.file}&type=file`}}
|
||||
},
|
||||
info() {
|
||||
if(!this.file) return
|
||||
this.showmodal = {vbind: {file: this.file}, component: 'media/ImageAttr',
|
||||
title: 'Thông tin trích xuất từ hình ảnh', width: '35%', height: '50vh'}
|
||||
},
|
||||
find() {
|
||||
this.showmodal = {vbind: {row: this.row, api: this.api, pagename: this.pagename}, component: 'ekyc/IDcheck',
|
||||
title: 'Kiểm tra CMT / CCCD / HC / GPLX / CMT-CA / CMT-QD', width: '90%', height: '70vh'}
|
||||
},
|
||||
download() {
|
||||
let name = this.file? this.file.file__name || this.file.name : 'download'
|
||||
let path = `${this.$getpath()}download/?name=${this.file.file__file || this.file.file}&type=file`
|
||||
this.$download(path, name)
|
||||
},
|
||||
askConfirm() {
|
||||
let text = this.store.lang==='en'? 'Do you agree to delete this image' : `Bạn có đồng ý <b>xóa hình ảnh</b> này không?`
|
||||
this.showmodal = {component: `dialog/Confirm`,title: this.store.lang==='en'? 'Confirm' : 'Xác nhận', width: '500px', height: '100px',
|
||||
vbind: {content: text, duration: 10}}
|
||||
},
|
||||
remove() {
|
||||
this.showmodal = undefined
|
||||
this.$emit('modalevent', {name: 'remove'})
|
||||
this.$emit('remove')
|
||||
},
|
||||
async copyImage() {
|
||||
try {
|
||||
const response = await fetch(`${this.$getpath()}download/?name=${this.file.file__file || this.file.file}&type=file`);
|
||||
const blob = await response.blob()
|
||||
await navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
[blob.type]: blob
|
||||
})
|
||||
])
|
||||
this.$snackbar('Copied to clipboard')
|
||||
} catch (err) {
|
||||
console.error(err.name, err.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
91
components/media/CropImage.vue
Normal file
91
components/media/CropImage.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="tile is-ancestor py-5 px-3 mx-0" v-if="image">
|
||||
<div class="tile is-1"/>
|
||||
<div class="tile is-7">
|
||||
<Cropper
|
||||
ref="cropper"
|
||||
:src="image"
|
||||
@change="onChange"
|
||||
:stencil-props="getRatio" />
|
||||
</div>
|
||||
<div class="tile is-1"> </div>
|
||||
<div class="tile">
|
||||
<div v-if="avatar!==true">
|
||||
<p class="mt-2 fs-16">Chọn tỷ lệ hoặc nhập chiều rộng và cao</p>
|
||||
<div class="tags are-medium mt-2">
|
||||
<a :class="curRatio.k===v.k? 'tag is-primary' : 'tag'" v-for="(v,i) in ratios" :key="i" @click="curRatio=v">{{v.k}}</a>
|
||||
</div>
|
||||
<div class="block mt-5">
|
||||
<b-radio v-model="radio"
|
||||
native-value="replace">
|
||||
Ghi đè
|
||||
</b-radio>
|
||||
<b-radio v-model="radio" class="ml-3"
|
||||
native-value="new">
|
||||
Tạo file mới
|
||||
</b-radio>
|
||||
</div>
|
||||
<p class="mt-4">
|
||||
<a class="button is-primary mr-4 mb-2" :class="loading? 'is-loading' : ''" @click="updateImage()">Lưu lại</a>
|
||||
</p>
|
||||
<p class="mt-2" v-if="coordinates">
|
||||
Hình ảnh cắt, {{'W: ' + coordinates.width + ', H: ' + coordinates.height + ', W/H: ' + coordinates.ratio}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="is-italic has-text-grey" v-else>* Di chuyển khung để chọn hình ảnh phù hợp</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { CircleStencil, Cropper } from 'vue-advanced-cropper'
|
||||
import 'vue-advanced-cropper/dist/style.css'
|
||||
export default {
|
||||
components: {
|
||||
Cropper,
|
||||
CircleStencil
|
||||
},
|
||||
props: ['selected', 'image', 'avatar'],
|
||||
data() {
|
||||
return {
|
||||
coordinates: undefined,
|
||||
ratios: [{k: '1/1'}, {k: '5/4'}, {k:'4/3'}, {k: '3/2'}, {k: '5/3'}, {k:'16/9'}, {k: '2/1'}, {k: '3/1'}, {k: '4/5'}, {k: '3/4'}, {k: '2/3'}, {k: '3/5'}, {k: '9/16'}, {k: '1/2'}, {k: '1/3'}],
|
||||
curRatio: {k: '1/1'},
|
||||
radio: this.avatar? 'new' : 'replace',
|
||||
rectangle: true,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getRatio() {
|
||||
return {aspectRatio: this.$calc(this.curRatio.k)}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange({ coordinates, canvas}) {
|
||||
this.coordinates = coordinates
|
||||
this.coordinates.ratio = (this.coordinates.width*1.00 / this.coordinates.height).toFixed(2)
|
||||
},
|
||||
updateImage() {
|
||||
const { canvas } = this.$refs.cropper.getResult()
|
||||
if (canvas) canvas.toBlob(blod=>this.saveAs(blod))
|
||||
},
|
||||
async saveAs(blod) {
|
||||
this.loading = true
|
||||
var form = new FormData();
|
||||
let name = this.selected.file.indexOf('-')>0? this.selected.file.substring(15, this.selected.file.length) : this.selected.file
|
||||
this.fileName = this.$dayjs(new Date()).format("YYYYMMDDhhmmss") + '-' + name
|
||||
if(this.radio==='replace') this.fileName = this.selected.file
|
||||
form.append('filename', this.fileName)
|
||||
form.append('name', name)
|
||||
form.append('file', blod)
|
||||
form.append('type', 'file')
|
||||
form.append('size', this.selected.size)
|
||||
form.append('user', this.$store.state.login.id)
|
||||
let result = await this.$insertapi('upload', form)
|
||||
this.loading = false
|
||||
if(result==='error') return
|
||||
this.$emit('modalevent', {name: 'image', data: result.rows[0]})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
32
components/media/FileCount.vue
Normal file
32
components/media/FileCount.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div>
|
||||
<span class="tooltip">
|
||||
<span class="dot-twitter" @click="doClick()">{{ row[attr] || 0 }}</span>
|
||||
<span class="tooliptext">Hồ sơ</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['row', 'api', 'pagename', 'attr'],
|
||||
methods: {
|
||||
async doClick() {
|
||||
let obj
|
||||
if(this.attr==='image_count') {
|
||||
let rs = await this.$getdata(this.api, {ref: this.row.id, file__type: 2})
|
||||
if(rs.length>0) {
|
||||
obj = {component: 'media/ImageShow', title: 'Hồ sơ', width: '80%', height: '70vh',
|
||||
vbind: {row: this.row, image: rs.map(v=>v.file__file), pagename: this.pagename, api: this.api}}
|
||||
} else {
|
||||
obj = {component: 'media/ImageGallery', vbind: {row: this.row, pagename: this.pagename, api: this.api},
|
||||
width: '50%', height: '500px', title: 'Hình ảnh'}
|
||||
}
|
||||
} else {
|
||||
let vbind = {row: this.row, api: this.api, setting: 'media-fields', filter: {ref: this.row.id, file__type: 1}}
|
||||
obj = {component: 'media/FileList', title: 'Hồ sơ', width: '50%', height: '300px', vbind: vbind}
|
||||
}
|
||||
this.$emit('open', {name: 'dataevent', data: {modal: obj}})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
49
components/media/FileGallery.vue
Normal file
49
components/media/FileGallery.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div v-if="files">
|
||||
<div class="has-text-right" v-if="!hideopt">
|
||||
<FileUpload v-bind="{type: 'file', position: position}" @files="getFiles"></FileUpload>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mt-3 has-text-grey fs-15" v-if="files.length===0 && info!==false">
|
||||
Chưa có tài liệu được tải lên
|
||||
</div>
|
||||
<DataView v-bind="vbind" v-else-if="vbind"></DataView>
|
||||
</div>
|
||||
<Modal @close="showmodal=undefined" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['row', 'pagename', 'api', 'hideopt', 'setting', 'info', 'position'],
|
||||
data() {
|
||||
return {
|
||||
showmodal: undefined,
|
||||
files: undefined,
|
||||
vbind: undefined
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.files = await this.$getdata(this.api, {ref: this.row.id, file__type: 1})
|
||||
if(this.files.length>0) this.vbind = {api: this.api, setting: this.setting || 'media-fields', data: this.files}
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.showmodal = {component: 'media/Imagebox', width: '70%', title: 'Chọn hình ảnh', height: '500px'}
|
||||
},
|
||||
async remove(v, i) {
|
||||
this.$delete(this.files, i)
|
||||
await this.$deleteapi(this.api, v.id)
|
||||
},
|
||||
async getFiles(files) {
|
||||
let arr = files.map(v=>{return {ref: this.row.id, file: v.id}})
|
||||
let found = this.$findapi(this.api)
|
||||
let rs = await this.$insertapi(this.api, arr, found.params.values)
|
||||
if(rs!=='error') {
|
||||
this.files = this.files.concat(rs)
|
||||
this.vbind = undefined
|
||||
setTimeout(()=>this.vbind = {api: this.api, setting: 'media-fields', data: this.files}, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
37
components/media/FileInfo.vue
Normal file
37
components/media/FileInfo.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div v-if="record">
|
||||
<div class="field">
|
||||
<label class="label">Tên<span class="has-text-danger"> * </span> </label>
|
||||
<p class="control">
|
||||
<input class="input" type="text" placeholder="Nhập số" v-model="record.name">
|
||||
</p>
|
||||
</div>
|
||||
<div class="field mt-5">
|
||||
<label class="label">Ghi chú<span class="has-text-danger"> * </span> </label>
|
||||
<p class="control">
|
||||
<textarea class="textarea" placeholder="" rows="3" v-model="record.caption"></textarea>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<button class="button is-primary" @click="save()">Lưu lại</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['row', 'pagename'],
|
||||
data() {
|
||||
return {
|
||||
record: undefined
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.record = await this.$getdata('file', {id: this.row.file || this.row.id}, undefined, true)
|
||||
},
|
||||
methods: {
|
||||
async save() {
|
||||
let rs = await this.$updateapi('file', this.record)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
47
components/media/FileList.vue
Normal file
47
components/media/FileList.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div>
|
||||
<FileUpload v-bind="{type: 'file'}" @files="getFiles"></FileUpload>
|
||||
<DataView v-if="vbind" v-bind="vbind"></DataView>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['row', 'pagename', 'api', 'setting'],
|
||||
data() {
|
||||
return {
|
||||
vbind: undefined,
|
||||
files: undefined,
|
||||
pagename1: this.$findpage()
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.pagedata = this.$getpage()
|
||||
this.files = await this.$getdata(this.api, {ref: this.row.id, file__type: 1})
|
||||
this.vbind = {pagename: this.pagename1, api: this.api, setting: this.setting, data: this.$copy(this.files)}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$clearpage(this.pagename1)
|
||||
},
|
||||
computed: {
|
||||
pagedata: {
|
||||
get: function() {return this.$store.state[this.pagename1]},
|
||||
set: function(val) {this.$store.commit('updateStore', {name: this.pagename1, data: val})}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getFiles(files) {
|
||||
let arr = files.map(v=>{return {ref: this.row.id, file: v.id}})
|
||||
let found = this.$findapi(this.api)
|
||||
let rs = await this.$insertapi(this.api, arr, found.params.values)
|
||||
if(rs==='error') return
|
||||
this.files = this.files.concat(rs)
|
||||
this.$store.commit('updateState', {name: this.pagename1, key: 'update', data: {data: this.$copy(this.files)}})
|
||||
if(this.pagename) {
|
||||
let vapi = this.api.replace('file', '')
|
||||
let ele = await this.$getdata(vapi, {id: this.row.id}, undefined, true)
|
||||
this.$updatepage(this.pagename, ele)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
42
components/media/FileShow.vue
Normal file
42
components/media/FileShow.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<p class="py-1 border-bottom" v-for="(v,i) in vfiles">
|
||||
<a class="mr-4" @click="open(v)">{{ v.name }}</a>
|
||||
<a class="mr-4" @click="download(v, i)">
|
||||
<SvgIcon v-bind="{name: 'download1.svg', type: 'dark', size: 16}"></SvgIcon>
|
||||
</a>
|
||||
<a @click="remove(v, i)" v-if="show? show.delete : false">
|
||||
<SvgIcon v-bind="{name: 'bin1.svg', type: 'dark', size: 16}"></SvgIcon>
|
||||
</a>
|
||||
</p>
|
||||
<Modal @close="showmodal=undefined" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||
</template>
|
||||
<script setup>
|
||||
const { $copy, $getpath, $download } = useNuxtApp()
|
||||
const emit = defineEmits(['remove', 'close'])
|
||||
var props = defineProps({
|
||||
files: Object,
|
||||
show: Object
|
||||
})
|
||||
var showmodal = ref()
|
||||
var vfiles = ref($copy(props.files))
|
||||
function remove(v, i) {
|
||||
vfiles.value.splice(i, 1)
|
||||
emit('remove', {v: v, i: i})
|
||||
emit('modalevent', {name: 'removefile', data: {v: v, i: i}})
|
||||
if(vfiles.value.length===0) emit('close')
|
||||
}
|
||||
function open(v) {
|
||||
if(v.name.indexOf('.png')>=0 || v.name.indexOf('.jpg')>=0 || v.name.indexOf('.jpeg')>=0) {
|
||||
showmodal.value = {title: v.file__file || v.file, component: 'media/ChipImage',
|
||||
vbind: {extend: false, file: v, image: `${$getpath()}download/?name=${v.file__file || v.file}&type=file`}}
|
||||
return
|
||||
}
|
||||
window.open(`${$getpath()}download/?name=${v.file__file || v.file}&type=file`)
|
||||
}
|
||||
function download(v) {
|
||||
$download(`${$getpath()}download/?name=${v.file}&type=file`, v.name)
|
||||
}
|
||||
watch(() => props.files, (newVal, oldVal) => {
|
||||
vfiles.value = props.files
|
||||
})
|
||||
</script>
|
||||
69
components/media/FileUpload.vue
Normal file
69
components/media/FileUpload.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="file is-primary" id="ignore" v-if="position==='left'">
|
||||
<label class="file-label">
|
||||
<input class="file-input" type="file" :id="docid" multiple name="resume" @change="doChange">
|
||||
<span class="file-cta px-2">
|
||||
<span class="icon-text is-clickable">
|
||||
<SvgIcon v-bind="{name: 'attach-file.svg', type: 'white', size: 22}"></SvgIcon>
|
||||
<!--<span class="has-text-white">File</span>-->
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="field is-grouped is-grouped-right" id="ignore" v-else>
|
||||
<div class="control">
|
||||
<div class="file is-primary">
|
||||
<label class="file-label">
|
||||
<input class="file-input" type="file" :id="docid" multiple name="resume" @change="doChange">
|
||||
<span class="file-cta px-1">
|
||||
<span class="icon-text is-clickable">
|
||||
<SvgIcon v-bind="{name: 'attach-file.svg', type: 'white', size: 22}"></SvgIcon>
|
||||
<!--<span class="has-text-white">File</span>-->
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Modal @close="showmodal=undefined" v-bind="showmodal" v-if="showmodal" @files="getFiles"></Modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['type', 'position'],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
files: undefined,
|
||||
dataFiles: [],
|
||||
vtype: this.type || 'image',
|
||||
showmodal: undefined,
|
||||
docid: this.$id()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doChange() {
|
||||
this.dataFiles = []
|
||||
let fileList = document.getElementById(this.docid).files
|
||||
this.files = Array.from(fileList)
|
||||
if(this.files.length===0) return
|
||||
this.files.map(v=>{
|
||||
let file = this.$upload(v, this.vtype, 1, '0') //this.$store.state.login.id
|
||||
this.dataFiles.push(file)
|
||||
})
|
||||
this.showmodal = {component: 'media/UploadProgress', title: 'Upload files', width: '700px', height: '200px', vbind: {files: this.dataFiles}}
|
||||
this.clearFileList()
|
||||
},
|
||||
clearFileList() {
|
||||
const fileInput = document.getElementById(this.docid);
|
||||
const dt = new DataTransfer()
|
||||
fileInput.files = dt.files
|
||||
},
|
||||
getFiles(files) {
|
||||
this.$emit('files', files)
|
||||
setTimeout(()=>this.showmodal=undefined, 3000)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
41
components/media/ImageAttr.vue
Normal file
41
components/media/ImageAttr.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div>
|
||||
<table class="table" v-if="attrs">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="170px">Thông tin</th>
|
||||
<th>Diễn giải</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="v in attrs">
|
||||
<td>{{ v.name }}</td>
|
||||
<td>
|
||||
<span class="icon-text">
|
||||
<span>{{ v.value }}</span>
|
||||
<span class="ml-5" v-if="!$empty(v.match)">
|
||||
<SvgIcon v-bind="{name: v.match? 'check2.svg' : 'error.svg', type: v.match? 'blue' : 'danger', size: 26}"></SvgIcon>
|
||||
</span>
|
||||
<span class="ml-5" v-if="!$empty(v.auth)">
|
||||
<SvgIcon v-bind="{name: v.auth? 'check2.svg' : 'error.svg', type: v.auth? 'blue' : 'danger', size: 26}"></SvgIcon>
|
||||
</span>
|
||||
<span class="ml-5" v-if="!$empty(v.expiry)">
|
||||
<SvgIcon v-bind="{name: v.expiry? 'error.svg' : 'check2.svg', type: v.expiry? 'danger' : 'blue', size: 26}"></SvgIcon>
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['file'],
|
||||
data() {
|
||||
return {
|
||||
attrs: this.file? this.file.file__verified_info || this.file.verified_info: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
54
components/media/ImageGallery.vue
Normal file
54
components/media/ImageGallery.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div v-if="files">
|
||||
<div class="has-text-right" v-if="!hideopt">
|
||||
<FileUpload @files="getFiles"></FileUpload>
|
||||
</div>
|
||||
<div class="field is-grouped is-grouped-multiline" v-if="files.length>0">
|
||||
<div class="control mb-2" v-for="(v,i) in files">
|
||||
<ChipImage style="width: 128px;" @remove="remove(v,i)"
|
||||
v-bind="{row: row, show: show, pagename: pagename, api: api, file: v, image: `${$getpath()}download/?name=${v.file__file}&type=file`}"></ChipImage>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 has-text-grey f5-16" v-if="files.length===0">
|
||||
{{ store.lang==='en'? 'No images' : "Chưa có hình ảnh"}}
|
||||
</div>
|
||||
<Modal @close="showmodal=undefined" v-bind="showmodal" v-if="showmodal"></Modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { useStore } from '~/stores/index'
|
||||
export default {
|
||||
setup() {
|
||||
const store = useStore()
|
||||
return { store }
|
||||
},
|
||||
props: ['row', 'pagename', 'api', 'hideopt', 'vapi', 'show'],
|
||||
data() {
|
||||
return {
|
||||
showmodal: undefined,
|
||||
files: undefined
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.files = await this.$getdata(this.api, {ref: this.row.id, file__type: 2})
|
||||
},
|
||||
methods: {
|
||||
async remove(v, i) {
|
||||
this.$remove(this.files, i)
|
||||
await this.$deleteapi(this.api, v.id)
|
||||
},
|
||||
async getFiles(files) {
|
||||
let arr = files.map(v=>{return {ref: this.row.id, file: v.id}})
|
||||
let found = this.$findapi(this.api)
|
||||
let rs = await this.$insertapi(this.api, arr, found.params.values)
|
||||
if(rs==='error') return
|
||||
this.files = this.files.concat(rs)
|
||||
if(this.pagename) {
|
||||
let vapi = this.vapi? this.vapi : this.api.replace('file', '')
|
||||
let ele = await this.$getdata(vapi, {id: this.row.id}, undefined, true)
|
||||
this.$updatepage(this.pagename, ele)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
58
components/media/ImageShow.vue
Normal file
58
components/media/ImageShow.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="columns mx-0" v-if="picture">
|
||||
<div class="column is-narrow" style="border-right: 2px solid #D3D3D3;" v-if="viewport>1">
|
||||
<div class="pr-2" style="max-height: 700px; overflow-y: auto;">
|
||||
<div v-for="(v,i) in picture" class="pb-4">
|
||||
<figure class="image is-clickable" style="width:80px" @click="current=i">
|
||||
<img :src="v.image">
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<b-carousel :autoplay="false" :indicator="viewport===1? true : false" icon-size="is-medium" v-model="current">
|
||||
<b-carousel-item v-for="(item, i) in picture" :key="i">
|
||||
<ChipImage @remove="check(item, i)" v-bind="{extend: false, image: item.image, file: item.file, show: ['download', 'delete']}"></ChipImage>
|
||||
</b-carousel-item>
|
||||
</b-carousel>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['row', 'image', 'pagename', 'api'],
|
||||
data() {
|
||||
return {
|
||||
current: 0,
|
||||
picture: undefined,
|
||||
timer: undefined
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
let arr = []
|
||||
let files = await this.$getdata('file', {file__in: this.image})
|
||||
this.image.map(v=>{
|
||||
let found = this.$find(files, {file: v})
|
||||
arr.push({image: `${this.$getpath()}download/?name=${v}`, file: found})
|
||||
})
|
||||
this.picture = arr
|
||||
},
|
||||
computed: {
|
||||
viewport: {
|
||||
get: function() {return this.$store.state.viewport},
|
||||
set: function(val) {this.$store.commit("updateViewPort", {viewport: val})}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
check(v, i) {
|
||||
if(!this.timer) this.timer = setTimeout(()=>this.remove(v,i), 200)
|
||||
},
|
||||
async remove(v, i) {
|
||||
this.$delete(this.picture, i)
|
||||
await this.$deleteapi(this.api, v.file.id)
|
||||
this.timer = undefined
|
||||
if(this.picture.length===0) this.$emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
288
components/media/Imagebox.vue
Normal file
288
components/media/Imagebox.vue
Normal file
@@ -0,0 +1,288 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="is-hidden"><img id="image" :src="image"></div>
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="file is-primary">
|
||||
<label class="file-label">
|
||||
<input class="file-input" type="file" :id="docid" multiple name="resume" @change="doChange">
|
||||
<a class="button is-primary is-small has-text-white" :class="loading? 'is-loading' : null">
|
||||
<SvgIcon v-bind="{name: 'add5.svg', type: 'white', size: 18}"></SvgIcon>
|
||||
<span class="fs-14 ml-1">Tải lên từ máy tính</span>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="`control ${showUrl? '' : 'is-expanded'}`">
|
||||
<a class="button is-dark is-small has-text-white" @click="displayInput()">
|
||||
<SvgIcon v-bind="{name: 'add5.svg', type: 'white', size: 18}"></SvgIcon>
|
||||
<span class="fs-14 ml-1">Tải lên từ đường dẫn</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="control is-expanded" v-if="showUrl">
|
||||
<input class="input is-small" id="url" v-model="url" style="width: 250px;" type="text" placeholder="Nhập đường dẫn vào đây">
|
||||
<a class="button is-primary is-small px-4 ml-3" @click="checkUrl()">
|
||||
<SvgIcon v-bind="{name: 'upload.svg', type: 'white', size: 22}"></SvgIcon>
|
||||
</a>
|
||||
<a class="ml-4" @click="showUrl=false">
|
||||
<SvgIcon v-bind="{name: 'close.svg', type: 'dark', size: 22}"></SvgIcon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="control">
|
||||
<input class="input is-small" v-model="search" style="width: 250px;" type="text" placeholder="Tìm kiếm" @keyup="beginSearch">
|
||||
</div>
|
||||
<div class="control">
|
||||
<span class="is-clickable" @click="mode='image'">
|
||||
<SvgIcon v-bind="{name: 'image3.svg', type: 'dark', size: 25}"></SvgIcon>
|
||||
</span>
|
||||
</div>
|
||||
<div class="control">
|
||||
<span class="is-clickable" @click="mode='list'">
|
||||
<SvgIcon v-bind="{name: 'list.png', type: 'dark', size: 25}"></SvgIcon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<DataView class="mt-3" v-bind="vbind" v-if="mode==='list'"></DataView>
|
||||
<div class="columns is-multiline mx-0" v-else>
|
||||
<div class="column is-2" v-for="k in data">
|
||||
<span class="tooltip">
|
||||
<nuxt-img :src="`${path}download?name=${k.file}`" :id="'commentImage' + k.id"></nuxt-img>
|
||||
<span class="tooltiptext">{{ k.name }}</span>
|
||||
</span>
|
||||
<div>
|
||||
<a class="mr-4" @click="selectMedia(k)">
|
||||
<SvgIcon v-bind="{name: 'checked.svg', type: 'gray', size: 22}"></SvgIcon>
|
||||
</a>
|
||||
<a class="mr-4" @click="copyMedia(k, 'image')">
|
||||
<SvgIcon v-bind="{name: 'copy.svg', type: 'gray', size: 22}"></SvgIcon>
|
||||
</a>
|
||||
<a class="mr-4" @click="copyMedia(k, 'image')">
|
||||
<SvgIcon v-bind="{name: 'download1.svg', type: 'gray', size: 22}"></SvgIcon>
|
||||
</a>
|
||||
<a class="mr-4" @click="editImage(k)">
|
||||
<SvgIcon v-bind="{name: 'crop.svg', type: 'gray', size: 22}"></SvgIcon>
|
||||
</a>
|
||||
<a @click="deleteMedia(k, 'file')">
|
||||
<SvgIcon v-bind="{name: 'bin.svg', type: 'gray', size: 22}"></SvgIcon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Modal @close="showmodal=undefined" v-bind="showmodal" @image="updateImage" @files="getFiles" v-if="showmodal"/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { useStore } from '~/stores/index'
|
||||
export default {
|
||||
setup() {
|
||||
const store = useStore()
|
||||
return {store}
|
||||
},
|
||||
props: ['source'],
|
||||
data() {
|
||||
return {
|
||||
file: undefined,
|
||||
image: undefined,
|
||||
showmodal: undefined,
|
||||
data: [],
|
||||
group: [],
|
||||
focus: undefined,
|
||||
type: 'image',
|
||||
selected: undefined,
|
||||
loading: false,
|
||||
path: this.$getpath(),
|
||||
showUrl: false,
|
||||
url: undefined,
|
||||
search: undefined,
|
||||
mode: 'image',
|
||||
timer: undefined,
|
||||
vbind: {api: 'file', setting: 'image-fields'},
|
||||
dataFiles: [],
|
||||
files: [],
|
||||
docid: this.$id()
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
let found = this.$findapi('file')
|
||||
found.params = {filter: {user: this.login.id}, sort: '-create_time', type__code: this.type}
|
||||
let result = await this.$getapi([found])
|
||||
this.data = result[0].data.rows
|
||||
},
|
||||
watch: {
|
||||
data: function() {
|
||||
this.group = []
|
||||
for (let index = 0; index < this.data.length/ (this.type==='video'? 4 : 5) ; index++) {
|
||||
this.group.push(index)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
login: {
|
||||
get: function() {return this.store.login},
|
||||
set: function(val) {this.store.commit("login", val)}
|
||||
},
|
||||
media: {
|
||||
get: function() {return this.store.media},
|
||||
set: function(val) {this.store.commit("media", val)}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doChange() {
|
||||
this.dataFiles = []
|
||||
let fileList = document.getElementById(this.docid).files
|
||||
this.files = Array.from(fileList)
|
||||
if(this.files.length===0) return
|
||||
this.files.map(v=>{
|
||||
let file = this.$upload(v, this.vtype, this.store.login.id)
|
||||
this.dataFiles.push(file)
|
||||
})
|
||||
this.showmodal = {component: 'media/UploadProgress', title: 'Upload files', width: '700px', height: '200px', vbind: {files: this.dataFiles}}
|
||||
this.clearFileList()
|
||||
},
|
||||
clearFileList() {
|
||||
const fileInput = document.getElementById(this.docid)
|
||||
const dt = new DataTransfer()
|
||||
fileInput.files = dt.files
|
||||
},
|
||||
getFiles(files) {
|
||||
this.data = files.concat(this.data)
|
||||
setTimeout(()=>this.showmodal=undefined, 3000)
|
||||
},
|
||||
beginSearch(e) {
|
||||
if(this.timer) clearTimeout(this.timer)
|
||||
this.timer = setTimeout(() => this.startSearch(e.target.value), 150)
|
||||
},
|
||||
async startSearch(value) {
|
||||
let filter = {user: this.login.id}
|
||||
if(!this.$empty(value)) filter.name__icontains = value.toLowerCase()
|
||||
this.data = await this.$getdata('file', filter)
|
||||
},
|
||||
displayInput() {
|
||||
this.showUrl = true
|
||||
this.url = undefined
|
||||
setTimeout(()=>document.getElementById('url').focus(), 100)
|
||||
},
|
||||
checkUrl() {
|
||||
if(this.$empty(this.url)) return this.$snackbar(`Đường dẫn không hợp lệ`, undefined, 'Error')
|
||||
let self = this
|
||||
this.loading = true
|
||||
this.$axios.get(this.url, {responseType:"blob" })
|
||||
.then(function(response) {
|
||||
var reader = new window.FileReader()
|
||||
reader.onload = (e) => {
|
||||
self.image = e.target.result
|
||||
setTimeout(()=> self.doUpload(e.target.result), 100)
|
||||
}
|
||||
reader.readAsDataURL(response.data)
|
||||
self.loading = false
|
||||
})
|
||||
.catch(e => {
|
||||
self.$buefy.toast.open({duration: 3000, message: `Đường dẫn không hợp lệ`, type: 'is-danger'})
|
||||
self.loading = false
|
||||
})
|
||||
},
|
||||
doUpload() {
|
||||
const image = document.getElementById('image')
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = image.width
|
||||
canvas.height = image.height
|
||||
const ctx = canvas.getContext('2d')
|
||||
ctx.drawImage(image, 0, 0)
|
||||
if(canvas) canvas.toBlob(blod=>this.saveAs(blod))
|
||||
},
|
||||
async saveAs(blod) {
|
||||
var form = new FormData();
|
||||
const fileName = this.$dayjs(new Date()).format("YYYYMMDDhhmmss") + '-' + this.$id() + '.png'
|
||||
form.append('filename', fileName)
|
||||
form.append('name', `${this.$id()}.png`)
|
||||
form.append('file', blod)
|
||||
form.append('type', 'image')
|
||||
form.append('size', 100)
|
||||
form.append('user', this.store.login.id)
|
||||
let result = await this.$insertapi('upload', form)
|
||||
if(result==='error') return
|
||||
this.updateImage(result.rows[0])
|
||||
this.showUrl = false
|
||||
},
|
||||
async uploadImage(file) {
|
||||
this.loading = true
|
||||
let result = await this.$insertapi('upload', file.form)
|
||||
this.data.splice(0, 0, result.rows[0])
|
||||
this.loading = false
|
||||
},
|
||||
getData(i) {
|
||||
if(this.type==='video') {
|
||||
var list = this.data.slice(i*5, i*5+5)
|
||||
for (let index = 0; index < 5; index++) {
|
||||
if(list.length<index+1) list.push({file: undefined})
|
||||
}
|
||||
} else {
|
||||
list = this.data.slice(i*6, i*6+6)
|
||||
for (let index = 0; index < 6; index++) {
|
||||
if(list.length<index+1) list.push({file: undefined})
|
||||
}
|
||||
}
|
||||
return list
|
||||
},
|
||||
selectMedia(v) {
|
||||
let copy = this.media? this.$copy(this.media) : {}
|
||||
copy.type = 'image'
|
||||
copy.open = false
|
||||
copy.select = v
|
||||
this.media = copy
|
||||
let row = this.$copy(v)
|
||||
if(this.source) {
|
||||
let params = {name: v.file, type: 'file'}
|
||||
this.$axios.get(`${this.path}download/`, {params: params, responseType:"blob" })
|
||||
.then(function (response) {
|
||||
var reader = new window.FileReader()
|
||||
reader.onload = (e) => {
|
||||
fetch(e.target.result)
|
||||
.then(res => res.blob())
|
||||
.then(blob => {
|
||||
const file = new File([blob], v.name,{ type: "image/png" })
|
||||
row.source = {file: file}
|
||||
})
|
||||
}
|
||||
reader.readAsDataURL(response.data)
|
||||
})
|
||||
}
|
||||
this.$emit('modalevent', {name: 'selectimage', data: row})
|
||||
},
|
||||
editImage(v) {
|
||||
this.loading = true
|
||||
this.selected = v
|
||||
let self = this
|
||||
let params = {name: v.file, type: 'file'}
|
||||
this.$axios.get(`${this.path}download/`, {params: params, responseType:"blob" })
|
||||
.then(function (response) {
|
||||
var reader = new window.FileReader()
|
||||
reader.onload = (e) => {
|
||||
self.image = e.target.result
|
||||
self.showmodal = {component: 'media/CropImage', width: '65%', title: 'Cắt hình ảnh', vbind: {selected: self.selected, image: self.image}}
|
||||
}
|
||||
reader.readAsDataURL(response.data)
|
||||
self.loading = false
|
||||
})
|
||||
},
|
||||
copyMedia(v) {
|
||||
this.$copyToClipboard(`${this.path}download/?name=${v.file}`)
|
||||
},
|
||||
deleteMedia(v, name) {
|
||||
let self = this
|
||||
var remove = async function() {
|
||||
let result = await self.$deleteapi(name, v.id)
|
||||
let idx = self.data.findIndex(x=>x.id===v.id)
|
||||
self.$delete(self.data, idx)
|
||||
}
|
||||
this.$buefy.dialog.confirm({
|
||||
message: 'Bạn muốn xóa file: ' + v.file,
|
||||
onConfirm: () => remove()})
|
||||
},
|
||||
updateImage(v) {
|
||||
this.data.splice(0, 0, v)
|
||||
this.showmodal = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
54
components/media/Picture.vue
Normal file
54
components/media/Picture.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="image-container" v-if="picture">
|
||||
<img :src="picture" style="max-height: 250px !important;">
|
||||
<div class="image-label">
|
||||
<span class="is-clickable" @click="remove()">
|
||||
<SvgIcon v-bind="{name: 'bin.svg', type: 'danger', size: 30}"></SvgIcon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 130px; border-style: dashed; border-width: 1px;" v-else>
|
||||
<a @click="openImage()"><SvgIcon v-bind="{name: 'image.svg', type: 'grey', size: 120}"></SvgIcon></a>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<a @click="openCamera()">
|
||||
<SvgIcon v-bind="{name: 'camera.svg', type: 'dark', size: 28}"></SvgIcon>
|
||||
</a>
|
||||
</div>
|
||||
<Modal @close="showmodal=undefined" v-bind="showmodal" v-if="showmodal" @selectimage="selectImage"></Modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['file', 'image', 'show'],
|
||||
data() {
|
||||
return {
|
||||
showmodal: undefined,
|
||||
display: this.show || [],
|
||||
picture: this.image || undefined,
|
||||
vfile: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openImage() {
|
||||
this.showmodal = {component:'media/Imagebox', title: 'Thư viện hình ảnh', width: '90%', vbind: {source: true}}
|
||||
},
|
||||
selectImage(files) {
|
||||
this.showmodal = undefined
|
||||
let v = files
|
||||
this.picture = `${this.$path()}download/?name=${v.file__file || v.file}&type=file`
|
||||
v.image = this.$copy(this.picture)
|
||||
this.vfile = v
|
||||
this.$emit('picture', v)
|
||||
},
|
||||
remove() {
|
||||
this.vfile = undefined
|
||||
this.picture = undefined
|
||||
},
|
||||
openCamera() {
|
||||
this.showmodal = {component:'media/Camera', title: 'Chụp ảnh', width: '650px'}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
57
components/media/UploadProgress.vue
Normal file
57
components/media/UploadProgress.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="has-text-left">
|
||||
<p class="py-1 border-bottom" v-for="v in vfiles">
|
||||
<span class="icon-text">
|
||||
<span class="mr-5">{{ v.name }}</span>
|
||||
<SvgIcon v-bind="{name: 'check2.svg', type: 'primary', size: 22}" v-if="v.status==='success'"></SvgIcon>
|
||||
<SvgIcon v-bind="{name: 'error.svg', type: 'danger', size: 22}" v-else-if="v.status==='error'"></SvgIcon>
|
||||
</span>
|
||||
<span class="icon-text has-text-danger ml-6" v-if="v.error">
|
||||
<SvgIcon v-bind="{name: 'error.svg', type: 'danger', size: 22}"></SvgIcon>
|
||||
<span class="ml-1">{{ v.text }}</span>
|
||||
</span>
|
||||
<button class="button is-small is-primary is-loading px-0 ml-4" v-if="v.status==='uploading'">Loading</button>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['files'],
|
||||
data() {
|
||||
return {
|
||||
vfiles: this.$copy(this.files),
|
||||
data: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
let found = this.$find(this.vfiles, {error: true})
|
||||
if(!found) this.upload()
|
||||
},
|
||||
methods: {
|
||||
async doUpload(v, i) {
|
||||
let file = this.files[i]
|
||||
let rs = await this.$insertapi('upload', file.form, undefined, false)
|
||||
v.status = rs==='error'? 'error' : 'success'
|
||||
this.vfiles[i] = v
|
||||
let obj = rs.rows[0]
|
||||
obj.source = file
|
||||
this.data.push(obj)
|
||||
this.checkDone()
|
||||
},
|
||||
async upload() {
|
||||
this.vfiles.map((v,i)=>{
|
||||
v.status = 'uploading'
|
||||
this.vfiles[i] = v
|
||||
this.doUpload(v, i)
|
||||
})
|
||||
},
|
||||
checkDone() {
|
||||
let found = this.vfiles.find(v=>!v.status || v.status==='uploading')
|
||||
if(!found) {
|
||||
this.$emit('files', this.data)
|
||||
this.$emit('modalevent', {name: 'files', data: this.data})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
12
components/media/YoutubeVideo.vue
Normal file
12
components/media/YoutubeVideo.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="columns mx--0">
|
||||
<div class="column is-10">
|
||||
<iframe width="100%" height="600px" src="https://www.youtube.com/embed/tgbNymZ7vqY"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user