文章目录

  • 前言
  • 一、思考过程?
  • 二、完善
    • 搭建时遇到过的问题。
      • 实现页面
      • 1、组件类型页面
      • 1.1、接口调用
      • 2、组件页面
      • 2.1、组件接口
      • 2.2、组件上传(该功能点是照抄的一个网上的文件上传组件,该组件挺牛逼的,实现的效果挺好)
      • 2.3、文件上传
      • 3、排序页面
      • 4、中间界面(意在实现将组件拖拽实现,目前是借鉴网上的代码,后续再实现)
      • 将组件加入首页展示
      • 路由也需要添加

前言

为自己搭建一个可以自我思考的平台,其核心为“心想事成”。


一、思考过程?

此时是我已实现过得一个前端案例,其中借鉴网上的拖拽事件的介绍实现的功能。

二、完善

搭建时遇到过的问题。

其中使用图片上传功能,我想着是上传到本地的前端项目的assets上然后可以展示,但实际情况是element展示不出上传到assets文件夹下的图片。

实现页面

1、组件类型页面

<template><div><div style="margin-bottom: 15px"><el-autocompletev-model="name":fetch-suggestions="querySearch":clearable="false"value-key="value"class="inline-input w-50"placeholder="请输入组件类型名称"@select="handleSelect"@keyup.enter.native="getDataList(true)"/></div><div style="margin-bottom: 15px"><el-button type="primary" plain @click="handleAdd">新增</el-button></div><el-spacefillwrap:fill-ratio="fillRatio":direction="direction"style="width: 100%; margin-bottom: 15px;"><el-table v-loading="loading" :data="tableData" border><template slot="empty"><el-empty :image-size="100" description="暂无数据"></el-empty></template><el-table-column label="组件类型" align="center" prop="name"/><el-table-column label="排序" align="center" prop="sort"/><el-table-columnlabel="操作"align="center"class-name="small-padding fixed-width"><template #default="scope"><el-button link type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button><el-button link type="primary" size="small" @click="handleDelete(scope.row)">删除</el-button></template></el-table-column></el-table></el-space><el-spacefillwrap:fill-ratio="fillRatio":direction="direction"><el-paginationv-model:current-page="queryParams.current"v-model:page-size="queryParams.size":page-sizes="[100, 200, 300, 400]":small="small":disabled="disabled":background="background"layout="total, sizes, prev, pager, next, jumper":total="total"@size-change="handleSizeChange"@current-change="handleCurrentChange"/></el-space><el-dialog v-model="dialogFormVisible" @close="resetForm(dataFormRef)" :title="title" width="30%" center><el-form ref="dataFormRef" :model="dataForm" :rules="rules"><el-form-item label="组件类型" :label-width="formLabelWidth" prop="name"><el-input v-model="dataForm.name" placeholder="请输入组件类型" autocomplete="off" /></el-form-item>
<!--                <el-form-item label="排序" :label-width="formLabelWidth" prop="sort"><el-input-number v-model="dataForm.sort" :min="1" /></el-form-item>--></el-form><template #footer><span class="dialog-footer"><el-button @click="closeDialog(false, dataFormRef)">取消</el-button><el-button type="primary" @click="submitDataForm(dataFormRef)">确认</el-button></span></template></el-dialog></div>
</template>
<script lang="ts" setup>
import {onMounted, ref} from 'vue'
import {page as keywordPage} from '@/api/keyword'
import {page as keywordRelationPage} from '@/api/keywordRelation'
import {ElMessage, ElMessageBox, FormInstance, FormRules} from "element-plus";
import {page, delById, getById, save, update} from '@/api/componentType'interface RestaurantItem {value: stringcount: string
}const restaurants = ref < RestaurantItem[] > ([])
const querySearch = (queryString: string, cb: any) => {if (queryString && queryString != '') {let params = {current: 1,size: 10,keyword: queryString,}let keywordList = []params.keyword = queryStringkeywordRelationPage(params).then(res => {if (res.data.total > 0) {res.data.records.forEach(item => {let keyword: object = {key: item.keyword,value: item.keywordRelationSentence,count: item.useCount,createTime: item.createTime,}keywordList.push(keyword)})}const results = queryString? keywordList.filter(createFilter(queryString)): keywordList// call callback function to return suggestionscb(results)})} else {const results = queryString? restaurants.value.filter(createInitFilter(queryString)): restaurants.value// call callback function to return suggestionscb(results)}
}
const createFilter = (queryString: string) => {return (item) => {return (item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0)}
}
const createInitFilter = (queryString: string) => {return (restaurant: RestaurantItem) => {return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0)}
}
const loadAll = () => {let params = {current: 1,size: 10,}let keywordList = []keywordPage(params).then(res => {if (res.data.total > 0) {res.data.records.forEach(item => {let keyword = {value: item.keyword,count: item.useCount,createTime: item.createTime,}keywordList.push(keyword)})}})return keywordList;
}const handleSelect = (item: RestaurantItem) => {console.log(item, 'restaurantItem')
}onMounted(() => {restaurants.value = loadAll()
})const direction = ref('horizontal')
const fillRatio = ref(30)
const tableData = ref([])
const name = ref('')
const total = ref(0)
const loading = ref('')
const queryParams = ref({current: 1,size: 10,name: '',
})
const small = ref(false)
const background = ref(false)
const disabled = ref(false)
const getDataList = (reset) => {if (reset) {queryParams.value.current = 1}if (name.value) {queryParams.value.name = name.value}page(queryParams.value).then(resp => {tableData.value = resp.data.recordstotal.value = resp.data.total})
}
const handleSizeChange = (val: number) => {console.log(`${val} items per page`)queryParams.value.size = valgetDataList(true)
}
const handleCurrentChange = (val: number) => {console.log(`current page: ${val}`)queryParams.value.current = valgetDataList(false)
}
onMounted(() => {getDataList(true)
})const handleDelete = (row: object) => {ElMessageBox.confirm('请确认是否删除该组件类型?','提示',{confirmButtonText: '确认',cancelButtonText: '取消',type: 'warning',}).then(async () => {try {await delById(row.id)ElMessage({type: 'success',message: '删除成功!',})getDataList(false)} catch (e) {console.log(e)}}).catch(() => {ElMessage({type: 'info',message: '取消删除',})})
}import {reactive} from 'vue'const title = ref('新增组件类型')
const dialogFormVisible = ref(false)
const formLabelWidth = '140px'interface DataForm {id: string,name: string,// sort: string,
}
const dataFormRef = ref<FormInstance>()
const dataForm = reactive<DataForm>({id: '',name: '',// sort: '',
})const rules = reactive<FormRules<DataForm>>({name: [{ required: true, message: '请输入组件类型名', trigger: 'blur' },],/*sort: [{ required: true, message: '请输入密码', trigger: 'blur' },],*/
})const handleAdd = () => {title.value = '新增组件类型'dialogFormVisible.value = true
}const handleEdit = (row: object) => {title.value = '修改组件类型'getById(row.id).then(res => {if (res.code != 200) {return}dialogFormVisible.value = truedataForm.id = res.data.iddataForm.name = res.data.name// dataForm.sort = res.data.sort})
}const submitDataForm = async (formEl: FormInstance | undefined) => {if (!formEl) returnawait formEl.validate((valid, fields) => {if (valid) {if (dataForm.id && dataForm.id != '') {update(dataForm).then(res => {if (res.code != 200) {ElMessage.success("请求错误")return}ElMessage.success("修改成功")closeDialog(false, formEl)})} else {save(dataForm).then(res => {if (res.code != 200) {ElMessage.success("请求错误")return}ElMessage.success("新增成功")closeDialog(true, formEl)})}} else {console.log('error submit!', fields)}})
}const closeDialog = (reset: boolean, formEl: FormInstance | undefined) => {dialogFormVisible.value = falseresetForm(formEl)getDataList(reset)
}const resetForm = (formEl: FormInstance | undefined) => {if (!formEl) returnformEl.resetFields()
}</script>
<style scoped>
.el-button--text {margin-right: 15px;
}
.el-select {width: 300px;
}
.el-input {width: 300px;
}
.dialog-footer button:first-child {margin-right: 10px;
}
::v-deep .el-dialog{display: flex;flex-direction: column;margin:0 !important;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);max-height:calc(100% - 30px);max-width:calc(100% - 30px);
}
::v-deep  .el-dialog .el-dialog__body{flex:1;overflow: auto;
}
</style>

1.1、接口调用

import axios from '../utils/http'// 分页查询用户列表
export function page(params) {return axios('/componentType/page', { params }, "get");
}// 分页查询组件列表
export function list() {return axios('/componentType/list', null, "get");
}// 根据主键ID删除用户信息
export function delById(id) {return axios(`/componentType/delById/${id}`, null, "delete");
}// 根据主键ID查询用户详情
export function  getById(id) {return axios(`/componentType/${id}`, null, 'get')
}// 根据主键ID查询用户详情
export function save(dataForm) {return axios(`/componentType/save`, { dataForm }, 'post')
}// 根据主键ID查询用户详情
export function update(dataForm) {return axios(`/componentType/update`, { dataForm }, 'put')
}

2、组件页面

<template><div><div style="margin-bottom: 15px"><el-autocompletev-model="name":fetch-suggestions="querySearch":clearable="false"value-key="value"class="inline-input w-50"placeholder="请输入组件名"@select="handleSelect"@keyup.enter.native="getDataList(true)"/></div><div style="margin-bottom: 15px"><el-button type="primary" plain @click="handleAdd">新增</el-button></div><el-spacefillwrap:fill-ratio="fillRatio":direction="direction"style="width: 100%; margin-bottom: 15px;"><el-table v-loading="loading" :data="tableData" border><template slot="empty"><el-empty :image-size="100" description="暂无数据"></el-empty></template><el-table-column label="组件" align="center" prop="name"/><el-table-column label="排序" align="center" prop="sort"/><el-table-column label="组件图片" align="center" prop="imgUrl"><template #default="scope"><el-image style="width: 100px; height: 100px" :src="scope.row.imgUrl" :fit="'fill'" /></template></el-table-column><el-table-columnlabel="操作"align="center"class-name="small-padding fixed-width"><template #default="scope"><el-button link type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button><el-button link type="primary" size="small" @click="handleDelete(scope.row)">删除</el-button></template></el-table-column></el-table></el-space><el-spacefillwrap:fill-ratio="fillRatio":direction="direction"><el-pagination:hide-on-single-page="hidePageVisible"v-model:current-page="queryParams.current"v-model:page-size="queryParams.size":page-sizes="[10, 50, 100, 200]":small="small":disabled="disabled":background="background"layout="total, sizes, prev, pager, next, jumper":total="total"@size-change="handleSizeChange"@current-change="handleCurrentChange"/></el-space><el-dialog v-model="dialogFormVisible" @close="resetForm(dataFormRef)" :title="title" width="30%" center><el-form ref="dataFormRef" :model="dataForm" :rules="rules"><el-form-item label="组件" :label-width="formLabelWidth" prop="name"><el-input v-model="dataForm.name" placeholder="请输入组件名称" autocomplete="off" /></el-form-item><el-form-item label="组件类型" :label-width="formLabelWidth" prop="typeId"><el-select v-model="dataForm.typeId" class="m-2" placeholder="Select" filterable size="large"><el-optionv-for="item in componentTypeOptions":key="item.value":label="item.label":value="item.value"/></el-select></el-form-item>
<!--                <el-form-item label="排序" :label-width="formLabelWidth" prop="sort"><el-input v-model="dataForm.sort" placeholder="请输入排序" autocomplete="off" /></el-form-item>--><el-form-item label="组件图片" :label-width="formLabelWidth" prop="imgUrl"><my-upload-file :imgUrl="dataForm.imgUrl" @uploaded="getImgUrl"></my-upload-file></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="closeDialog(false, dataFormRef)">取消</el-button><el-button type="primary" @click="submitDataForm(dataFormRef)">确认</el-button></span></template></el-dialog></div>
</template>
<script lang="ts" setup>
import {onMounted, ref, reactive} from 'vue'
import {page as keywordPage} from '@/api/keyword'
import {page as keywordRelationPage} from '@/api/keywordRelation'
import {ElMessage, ElMessageBox, FormInstance, FormRules, UploadProps, UploadUserFile} from "element-plus";
import {page, delById, getById, save, update} from '@/api/component'
import type { UploadFile } from 'element-plus'
import {list} from "@/api/componentType";interface RestaurantItem {value: stringcount: string
}const componentTypeOptions = ref([])
const getComponentTypeList = () => {list().then(resp => {if (resp.code != 200) returnlet options = []resp.data.forEach((item, index) => {let option = {label: item.name,value: item.id}if (index == 0) {dataForm.typeId = item.id}options.push(option)})componentTypeOptions.value = options})
}
onMounted(() => {getComponentTypeList()
})const restaurants = ref < RestaurantItem[] > ([])
const querySearch = (queryString: string, cb: any) => {if (queryString && queryString != '') {let params = {current: 1,size: 10,keyword: queryString,}let keywordList = []params.keyword = queryStringkeywordRelationPage(params).then(res => {if (res.data.total > 0) {res.data.records.forEach(item => {let keyword: object = {key: item.keyword,value: item.keywordRelationSentence,count: item.useCount,createTime: item.createTime,}keywordList.push(keyword)})}const results = queryString? keywordList.filter(createFilter(queryString)): keywordList// call callback function to return suggestionscb(results)})} else {const results = queryString? restaurants.value.filter(createInitFilter(queryString)): restaurants.value// call callback function to return suggestionscb(results)}
}
const createFilter = (queryString: string) => {return (item) => {return (item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0)}
}
const createInitFilter = (queryString: string) => {return (restaurant: RestaurantItem) => {return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0)}
}
const loadAll = () => {let params = {current: 1,size: 10,}let keywordList = []keywordPage(params).then(res => {if (res.data.total > 0) {res.data.records.forEach(item => {let keyword = {value: item.keyword,count: item.useCount,createTime: item.createTime,}keywordList.push(keyword)})}})return keywordList;
}
const handleSelect = (item: RestaurantItem) => {console.log(item, 'restaurantItem')
}
onMounted(() => {restaurants.value = loadAll()
})const direction = ref('horizontal')
const fillRatio = ref(30)
const tableData = ref([])
const name = ref('')
const total = ref(0)
const loading = ref('')
const queryParams = ref({current: 1,size: 10,name: '',
})
const small = ref(false)
const background = ref(false)
const disabled = ref(false)
const hidePageVisible = ref(false)
const getDataList = (reset) => {if (reset) {queryParams.value.current = 1}if (name.value) {queryParams.value.name = name.value}page(queryParams.value).then(resp => {tableData.value = resp.data.recordstotal.value = resp.data.totalif (resp.data.total < queryParams.value.size) {hidePageVisible.value = true}})
}
const handleSizeChange = (val: number) => {console.log(`${val} items per page`)queryParams.value.size = valgetDataList(true)
}
const handleCurrentChange = (val: number) => {console.log(`current page: ${val}`)queryParams.value.current = valgetDataList(false)
}
onMounted(() => {getDataList(true)
})const handleDelete = (row: object) => {ElMessageBox.confirm('请确认是否删除该组件?','提示',{confirmButtonText: '确认',cancelButtonText: '取消',type: 'warning',}).then(async () => {try {await delById(row["id"])ElMessage({type: 'success',message: '删除成功!',})getDataList(false)} catch (e) {console.log(e)}}).catch(() => {ElMessage({type: 'info',message: '取消删除',})})
}
const title = ref('新增组件')
const dialogFormVisible = ref(false)
const formLabelWidth = '140px'
interface DataForm {id: string,name: string,typeId: Number,sort: Number,imgUrl: string
}
const dataFormRef = ref<FormInstance>()
const dataForm = reactive<DataForm>({id: '',name: '',typeId: 0,sort: 0,imgUrl: ''
})
const rules = reactive<FormRules<DataForm>>({name: [{ required: true, message: '请输入组件名', trigger: 'blur' },],typeId: [{ required: true, message: '请选择组件类型', trigger: 'blur' },],sort: [{ required: true, message: '请输入排序', trigger: 'blur' },],
})
const handleAdd = () => {title.value = '新增组件'dialogFormVisible.value = truedataForm.id = ''dataForm.name = ''
}
const handleEdit = (row: object) => {title.value = '修改组件'getById(row["id"]).then(res => {if (res.code != 200) {return}dataForm.id = res.data.iddataForm.name = res.data.namedataForm.sort = res.data.sortdataForm.imgUrl = res.data.imgUrldialogFormVisible.value = true})
}
const submitDataForm = async (formEl: FormInstance | undefined) => {if (!formEl) returnawait formEl.validate((valid, fields) => {if (valid) {if (dataForm.id && dataForm.id != '') {update(dataForm).then(res => {if (res.code != 200) {ElMessage.success("请求错误")return}ElMessage.success("修改成功")closeDialog(false, formEl)})} else {save(dataForm).then(res => {if (res.code != 200) {ElMessage.success("请求错误")return}ElMessage.success("新增成功")closeDialog(true, formEl)})}} else {console.log('error submit!', fields)}})
}
const closeDialog = (reset: boolean, formEl: FormInstance | undefined) => {dialogFormVisible.value = falseresetForm(formEl)getDataList(reset)
}
const resetForm = (formEl: FormInstance | undefined) => {if (!formEl) returnformEl.resetFields()dataForm.imgUrl = ''
}import MyUploadFile from "@/components/MyUploadFile.vue";
const getImgUrl = (url: string) => {dataForm.imgUrl = url
}
</script>
<style scoped>
.el-button--text {margin-right: 15px;
}
.el-select {width: 300px;
}
.el-input {width: 300px;
}
.dialog-footer button:first-child {margin-right: 10px;
}
::v-deep .el-dialog{display: flex;flex-direction: column;margin:0 !important;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);max-height:calc(100% - 30px);max-width:calc(100% - 30px);
}
::v-deep  .el-dialog .el-dialog__body{flex:1;overflow: auto;
}
.avatar-uploader .avatar {width: 178px;height: 178px;display: block;
}
</style><style>
.avatar-uploader .el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);
}.avatar-uploader .el-upload:hover {border-color: var(--el-color-primary);
}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;
}
.none-up /deep/ .el-upload--picture-card {display: none;
}
::v-deep .el-dialog{display: flex;flex-direction: column;margin:0 !important;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);max-height:calc(100%);max-width:calc(100%);
}
::v-deep  .el-dialog .el-dialog__body{flex:1;overflow: auto;
}
</style>

2.1、组件接口

import axios from '../utils/http'// 分页查询组件页码
export function page(params) {return axios('/component/page', { params }, "get");
}// 根据主键ID删除用户信息
export function delById(id) {return axios(`/component/delById/${id}`, null, "delete");
}// 根据主键ID查询用户详情
export function  getById(id) {return axios(`/component/${id}`, null, 'get')
}// 根据主键ID查询用户详情
export function save(dataForm) {return axios(`/component/save`, { dataForm }, 'post')
}// 根据主键ID查询用户详情
export function update(dataForm) {return axios(`/component/update`, { dataForm }, 'put')
}

2.2、组件上传(该功能点是照抄的一个网上的文件上传组件,该组件挺牛逼的,实现的效果挺好)

<template><div class="uploader"><inputtype="file"id="file-input"style="display: none"accept="image/*"@change="onImageAdded"/><divclass="card upload-card"@click="openFileDialog"v-if="!isThumbnailVisible"><svgclass="icon"width="28"height="28"viewBox="0 0 1024 1024"xmlns="http://www.w3.org/2000/svg"><pathfill="#8c939d"d="M480 480V128a32 32 0 0164 0v352h352a32 32 0 110 64H544v352a32 32 0 11-64 0V544H128a32 32 0 010-64h352z"></path></svg></div><div class="card thumbnail-card" v-show="isThumbnailVisible"><img src="" alt="缩略图" id="thumbnail" /><label class="success-label" v-show="isSuccessLabelVisible"><i class="success-icon"><svgclass="icon"width="12"height="12"viewBox="0 0 1024 1024"xmlns="http://www.w3.org/2000/svg"><pathfill="white"d="M406.656 706.944L195.84 496.256a32 32 0 10-45.248 45.248l256 256 512-512a32 32 0 00-45.248-45.248L406.592 706.944z"></path></svg></i></label><!-- 图标 --><div class="thumbnail-actions"><span class="thumbnail-preview" @click="handleThumbnailPreview"><svgclass="icon"width="20"height="20"viewBox="0 0 1024 1024"xmlns="http://www.w3.org/2000/svg"><pathfill="white"d="M795.904 750.72l124.992 124.928a32 32 0 01-45.248 45.248L750.656 795.904a416 416 0 1145.248-45.248zM480 832a352 352 0 100-704 352 352 0 000 704zm-32-384v-96a32 32 0 0164 0v96h96a32 32 0 010 64h-96v96a32 32 0 01-64 0v-96h-96a32 32 0 010-64h96z"></path></svg></span><span class="thumbnail-delete" @click="handleThumbnailRemove"><svgclass="icon"width="20"height="20"viewBox="0 0 1024 1024"xmlns="http://www.w3.org/2000/svg"><pathfill="white"d="M160 256H96a32 32 0 010-64h256V95.936a32 32 0 0132-32h256a32 32 0 0132 32V192h256a32 32 0 110 64h-64v672a32 32 0 01-32 32H192a32 32 0 01-32-32V256zm448-64v-64H416v64h192zM224 896h576V256H224v640zm192-128a32 32 0 01-32-32V416a32 32 0 0164 0v320a32 32 0 01-32 32zm192 0a32 32 0 01-32-32V416a32 32 0 0164 0v320a32 32 0 01-32 32z"></path></svg></span></div><!-- 进度条 --><el-progresstype="circle":percentage="progress"v-show="isProgressVisible":width="110"id="progress"/></div><vue-easy-lightboxmoveDisabled:visible="isLightBoxVisible":imgs="localImageUrl":index="index"@hide="handleLightboxHide"/></div>
</template><script>
import { ref, computed } from "vue";
import { uploadImage } from "../utils/uploadImg";
import { Plus } from "@element-plus/icons-vue";
import VueEasyLightbox from "vue-easy-lightbox";
import {ElMessage, ElMessageBox, FormInstance, FormRules} from "element-plus";export default {name: "MyUploadFile",emits: ["uploaded", "aboutToUpload", "removed"],components: { Plus, VueEasyLightbox },props: {imgUrl: {type: String,default: ''},},setup(props, context) {let progress = ref(0);let isLightBoxVisible = ref(false);let isProgressVisible = ref(false);let isSuccessLabelVisible = ref(false);let imageUrl = ref("");let localImageUrl = ref("");let index = ref(0);let isThumbnailVisible = computed(() => localImageUrl.value.length > 0);if (props.imgUrl) {imageUrl.value = localImageUrl.value = props.imgUrlsetTimeout(() => {let thumbnailEl = document.getElementById("thumbnail");if (thumbnailEl) {thumbnailEl.src = props.imgUrl}}, 1000)}function openFileDialog() {document.getElementById("file-input").click();}function onImageAdded() {let fileInput = document.getElementById("file-input");if (fileInput.files.length == 0) {return;}context.emit("aboutToUpload");let file = fileInput.files[0];setImageUrl(URL.createObjectURL(file));upload(file);}function setImageUrl(url) {let thumbnailEl = document.getElementById("thumbnail");thumbnailEl.src = localImageUrl.value = url;}function handleThumbnailRemove(file) {imageUrl.value = "";localImageUrl.value = "";context.emit("removed", file);}function handleThumbnailPreview() {isLightBoxVisible.value = true;}function handleLightboxHide() {isLightBoxVisible.value = false;}function upload(file) {progress.value = 0;isProgressVisible.value = true;isSuccessLabelVisible.value = false;uploadImage(file, progress).then((url) => {progress.value = 100;imageUrl.value = url.url;document.getElementById("thumbnail").src = url.url;context.emit("uploaded", url.url);setTimeout(() => {isProgressVisible.value = false;isSuccessLabelVisible.value = true;}, 200);},() => {isProgressVisible.value = false;localImageUrl.value = "";context.emit("uploaded", "");ElMessage.error("哎呀,图片上传出错啦~")});}return {progress,imageUrl,localImageUrl,index,isLightBoxVisible,isThumbnailVisible,isProgressVisible,isSuccessLabelVisible,handleThumbnailRemove,handleThumbnailPreview,handleLightboxHide,openFileDialog,onImageAdded,setImageUrl,};},
};
</script><style lang="less" scoped>
.uploader {display: flex;
}.card {background-color: #fbfdff;border: 1px dashed #c0ccda;border-radius: 6px;width: 148px;height: 148px;overflow: hidden;
}.upload-card {display: flex;justify-content: center;align-items: center;transition: all 0.3s;cursor: pointer;&:hover {border-color: #409eff;color: #409eff;}
}.thumbnail-card {border: 1px solid #c0ccda;position: relative;#thumbnail {width: 100%;height: 100%;object-fit: contain;display: inline;}.success-label {position: absolute;right: -15px;top: -6px;width: 40px;height: 24px;background: #67c23a;text-align: center;transform: rotate(45deg);box-shadow: 0 0 1pc 1px #0003;.success-icon {position: absolute;left: 13px;top: 1px;transform: rotate(-45deg);}}#progress {width: 100%;height: 100%;position: absolute;top: 0;left: 0;background: rgba(255, 255, 255, 0.7);:deep(.el-progress-circle) {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}}.thumbnail-actions {width: 100%;height: 100%;background: rgba(0, 0, 0, 0.5);opacity: 0;transition: all 0.4s ease;display: flex;justify-content: center;align-items: center;position: absolute;top: 0;left: 0;border-radius: 6px;.thumbnail-preview,.thumbnail-delete {cursor: pointer;margin: 0 8px;display: inline-block;}&:hover {opacity: 1;}}
}:deep(.vel-img) {box-shadow: 0 5px 20px 2px rgba(0, 0, 0, 0.35);
}
</style>

2.3、文件上传

import axios from "axios"
import { ElMessage } from 'element-plus'const service = axios.create({baseURL: "https://ip:port/ossClass",headers: {"Content-Type": "multipart/form-data"}
})service.interceptors.response.use(response => {const code = response.data.code || 200if (code === 200) {return response.data.data}let msg = response.data.code + " " + response.data.msgElMessage.error(msg)return Promise.reject('上传图片失败:' + msg)
})/*** 上传图片* @param {File} file 图片文件* @param {RefImpl} progress 上传进度* @returns promise*/
function uploadImage(file, progress) {let formData = new FormData();formData.append("file", file)return service({url: "/upload",method: "post",data: formData,onUploadProgress(event) {let v = Math.round(event.loaded / event.total * 100)progress.value = v == 100 ? 80 : v},})
}export { uploadImage }

3、排序页面

<template><div class="component-with-type-box"><transition-group name="drag" class="row-line"><ulv-for="(item, index) in componentTypeList"class="component-type-box":draggable="true"@dragstart="dragstart(1, index, null)"@dragenter="dragenterType($event, index, null)"@dragover="dragover($event)"@dragend="dragend()"@mouseover="topicTypeHover($event)"@mouseout="removeTopicTypeHover($event)":key="index"><div class="component-type"><div class="component-type-title">{{ item.name }}</div><span class="drag-sort">拖拽换序</span></div><liv-for="(component, i) in item.componentList"class="component-type-info":draggable="true"@dragstart="dragstart(2, index, i)"@dragenter="dragenter($event, index, i)"@dragover="dragover($event)"@dragend="dragend()"@mouseover="addNumberHover($event)"@mouseout="removeNumberHover($event)":key="i"><el-image style="width: 100px; height: 100px" :src="component.imgUrl" :fit="'fill'" @click="bigImage(component.imgUrl)" />{{ component ? component.name : "" }}</li><div style="clear: both; height: 0px"></div></ul></transition-group><el-image-viewerstyle="z-index:1500"v-if="showImageViewer"@close="closeBigImage":url-list="imageData"/></div>
</template><script lang="ts" setup>
import {onMounted, ref, reactive} from 'vue'
import {cwtList} from '@/api/sort'interface QueryParams {name: string
}
const queryParams = reactive<QueryParams>({name: '',
})
const componentTypeList = ref([])
onMounted(() => {cwtList(queryParams).then(res => {if (res.code != 200) returncomponentTypeList.value = res.data})
})
const dragType = ref(0)
const typeDefaultIndex = ref(0)
const typeIndex = ref(0)
const dragDefaultIndex = ref(0)
const dragIndex = ref(0)
const updateLi = ref(false)
const typeInfo = ref({})
const moveInfo = ref({})const showImageViewer = ref(false)
const imageData = ref([])
const bigImage = (imgUrl: any) => {imageData.value.push(imgUrl)showImageViewer.value = true
}
const closeBigImage = () => {showImageViewer.value = false
}/*** 拖拽开始* @param type 类型:1-题型,2-题目* @param index 题型下标* @param i 题目下标*/
const dragstart = (type, index, i) => {if (dragType.value !== 0) {if (dragType.value === 1) {typeDefaultIndex.value = index;typeIndex.value = index;typeInfo.value = JSON.parse(JSON.stringify(componentTypeList.value[index]));}} else {dragType.value = typeif (dragType.value === 1) {typeDefaultIndex.value = index;typeIndex.value = index;typeInfo.value = JSON.parse(JSON.stringify(componentTypeList.value[index]));} else {updateLi.value = truetypeDefaultIndex.value = index;typeIndex.value = index;dragDefaultIndex.value = i;dragIndex.value = i;componentTypeList.value[index].componentList[i].sort = "";moveInfo.value = componentTypeList.value[index].componentList[i];removeNumberHover(event);}}
}
// 拖拽停留位置 --- 增加拖拽效果
const dragover = (event: any) => {event.preventDefault();
}
// 拖拽鼠标释放
const dragenter = (event: any, index: number, i: number) => {event.preventDefault();if (dragType.value === 1) {componentTypeList.value.splice(typeIndex.value, 1);componentTypeList.value.splice(index, 0, typeInfo.value);// 排序变化后目标对象的索引变成源对象的索引typeIndex.value = index;} else {// 避免源对象触发自身的dragenter事件// 是否跨区域,启示集合下标和移动至集合下标位置不同if (typeIndex.value !== index) {componentTypeList.value[typeIndex.value].componentList.splice(dragIndex.value, 1);componentTypeList.value[index].componentList.splice(i, 0, moveInfo.value);// 排序变化后目标对象的索引变成源对象的索引} else if (dragIndex.value !== i) {componentTypeList.value[typeIndex.value].componentList.splice(dragIndex.value, 1);componentTypeList.value[typeIndex.value].componentList.splice(i, 0, moveInfo.value);// 排序变化后目标对象的索引变成源对象的索引}typeIndex.value = index;dragIndex.value = i;}
}
// 跨类型拖拽
const dragenterType = (event: any, index: number) => {event.preventDefault();if (dragType.value === 1) {// 避免源对象触发自身的dragenter事件// 是否跨区域,启示集合下标和移动至集合下标位置不同componentTypeList.value.splice(typeIndex.value, 1);componentTypeList.value.splice(index, 0, typeInfo.value);// 排序变化后目标对象的索引变成源对象的索引typeIndex.value = index;} else {// 避免源对象触发自身的dragenter事件// 是否跨区域,启示集合下标和移动至集合下标位置不同if (typeIndex.value !== index) {componentTypeList.value[typeIndex.value].componentList.splice(dragIndex.value, 1);componentTypeList.value[index].componentList.splice(componentTypeList.value[index].componentList.length, 0, moveInfo.value);// 排序变化后目标对象的索引变成源对象的索引typeIndex.value = index;dragIndex.value = componentTypeList.value[index].componentList.length - 1;}}
}
// 拖拽结束
const dragend = () => {if (dragType.value === 1) {componentTypeList.value.forEach((item, index) => {index++;item.sort = index/*item.componentList.forEach((component, i) => {i++;component.sort = i;});*/});} else {componentTypeList.value.forEach((item, index) => {index++;item.sort = index/*item.componentList.forEach((component, i) => {i++;component.sort = i;});*/});}dragType.value = 0
}
// 题号鼠标经过时添加样式
const addNumberHover = (event:any) => {event.currentTarget.className = "component-type-info-hover";
}
// 题号鼠标经过后移除样式
const removeNumberHover = (event:any) => {event.currentTarget.className = "component-type-info";
}
// 题型经过时添加样式
const topicTypeHover = (event:any) => {event.currentTarget.className = "component-type-box-hover";
}
// 题型经过后移除样式
const removeTopicTypeHover = (event:any) => {event.currentTarget.className = "component-type-box";
}
</script><style lang="scss" scoped>
$ratio: 0.85;
.component-with-type-box {display: flex;flex-direction: column;align-items: center;justify-content: center;border: 1px solid #78edaf;.component-type-box {min-width: 300px;list-style: none;padding: 10px;border: 1px solid #78edaf;.component-type {height: 40px;line-height: 40px;margin-bottom: 10px;display: flex;align-items: center;.component-type-title {display: flex;.item_nameBox {width: 80%;box-sizing: border-box;//padding: 15px * $ratio 0 15px * $ratio;// display: flex;// flex-direction: column;.item_title {}}}.drag-sort {display: none;}}}.component-type-box-hover {min-width: 300px;list-style: none;padding: 9px;border: 1px solid #78edaf;background: #F8FAFF;cursor: pointer;.component-type {height: 40px;line-height: 40px;margin-bottom: 10px;display: flex;align-items: center;.component-type-title {width: 85%;display: flex;.item_nameBox {width: 80%;box-sizing: border-box;//padding: 15px * $ratio 0 15px * $ratio;// display: flex;// flex-direction: column;.item_title {}}.item_title {display: flex;}i {color: #78edaf}}.drag-sort {display: inline-block;width: 60px;height: 20px;font-size: 14px;font-family: PingFangSC-Medium, PingFang SC;font-weight: 500;color: #989898;line-height: 20px;}}}.drag-move {transition: transform 0.3s;}.component-type-info {cursor: pointer;line-height: 27px;background: #ffffff;border-radius: 6px;border: 1px solid #78edaf;color: #78edaf;float: left;text-align: center;margin-right: 20px;margin-bottom: 20px;display: flex;flex-direction: column;::v-deep .el-image {border-radius: 6px;}}.component-type-info-hover {cursor: pointer;line-height: 27px;border-radius: 6px;border: 1px solid #78edaf;float: left;text-align: center;margin-right: 20px;margin-bottom: 20px;background: #78edaf;color: #ffffff;display: flex;flex-direction: column;::v-deep .el-image {border-radius: 6px;}}
}
</style>

4、中间界面(意在实现将组件拖拽实现,目前是借鉴网上的代码,后续再实现)

<template><div class="box"><!-- 左侧拖拽组件 --><!-- v-if="false" --><div class="drap"><!-- <p>元素</p> --><!--@dragstart  < -- 是元素开始拖拽的时候触发draggable="true"  < -- 为了使元素可拖动,把 draggable 属性设置为 true :@dragover.prevent < -- 阻止浏览器默认行为,不然会显示一个叉叉,不好看, 加上会显示一个添加的符号--><divv-for="(item, index) in drapLeftElList"class="drap-item":key="index"@dragstart="handleDrapEvList($event, item)"@dragover.preventdraggable="true"><imgclass="drap-item-img"draggable="false":src="item.imgUrl":alt="item.name"/><div class="drap-item-name">{{ item.name }}</div></div></div><!-- 主体部分 --><divclass="drap-container"@dragover.prevent@mousedown="laryerMouseDown"@mousemove="laryerMouseMove"@mouseup="laryerMouseUp"@drop="handleDrap"><h1>画布</h1><divv-for="(item, index) in componentsList"class="drap-container-item":class="{'drap-container-item-active':curControl && item.identifier == curControl.identifier,}":key="index":style="{top: `${item.position.y}px`,left: `${item.position.x}px`,width: `${item.position.w}px`,height: `${item.position.h}px`,'background-color': `${item.position.bg}`,}"@mousedown.stop="handleMouseDown($event, item, index)"><imgclass="drap-item-img":src="item.imgUrl"draggable="false":alt="item.name"/><div class="drap-item-name">{{ item.name }}</div></div></div><!-- 属性配置 --><div class="drap-right" style="width: 300px; height: 100%"><h2>属性配置</h2>{{ identifier }}<br />{{ curControl }}<br />{{ containerMoveObj }}</div></div>
</template><script>
export default {name: "drap",data() {return {// 保存拖拽的元素的列表componentsList: [{id: 11,name: "团队1",imgUrl:"https://wegosmart.oss-cn-shenzhen.aliyuncs.com/1717066356123369472.png",sort: 1,identifier: 666,position: {x: 100,y: 100,w: 80,h: 120,bg: "#ffffff",},style: {},temp: {position: {x: 100,y: 100,},},},],//   元件库drapLeftElList: [{id: 11,name: "团队1",imgUrl:"https://wegosmart.oss-cn-shenzhen.aliyuncs.com/1717066356123369472.png",sort: 1,position: {x: 0,y: 0,w: 80,h: 120,bg: "#fff",},temp: {position: {x: 0,y: 0,},},},{id: 13,name: "团队2",imgUrl:"https://wegosmart.oss-cn-shenzhen.aliyuncs.com/1717066356123369472.png",sort: 2,position: {x: 0,y: 0,w: 80,h: 120,bg: "#fff",},temp: {position: {x: 0,y: 0,},},},{id: 14,name: "团队3",imgUrl:"https://wegosmart.oss-cn-shenzhen.aliyuncs.com/1717066356123369472.png",sort: 3,position: {x: 0,y: 0,w: 80,h: 120,bg: "#fff",},temp: {position: {x: 0,y: 0,},},},{id: 15,name: "团队4",imgUrl:"https://wegosmart.oss-cn-shenzhen.aliyuncs.com/1717066356123369472.png",sort: 3,position: {x: 0,y: 0,w: 80,h: 120,bg: "#fff",},temp: {position: {x: 0,y: 0,},},},],identifier: "", // 当前项的 唯一标识curControl: null, //flag: "",containerMoveObj: {type: "",x: "",y: "",},};},methods: {// 点击画布的时候, 取消选择组件laryerMouseDown() {console.log("laryerMouseDown");this.curControl = null;},// 给画布绑定的mousemove事件laryerMouseMove(ev) {// 判断是需要移动的类型if (this.flag == "move") {// 用当前移动的距离减去点击的位置let dx = ev.pageX - this.containerMoveObj.x,dy = ev.pageY - this.containerMoveObj.y;// 上次旧的位置加上 处理完的距离就得到当前位置let x = this.curControl.temp.position.x + dx,y = this.curControl.temp.position.y + dy;// 这里只是让元素跟着鼠标移动, 如果再这里直接赋值this.curControl.position.x = x;this.curControl.position.y = y;}},// 给画布绑定的mouseup事件laryerMouseUp() {// 在鼠标抬起的时候判断是否if (this.flag == "") {return false;}const x = this.curControl.position.x;const y = this.curControl.position.y;// 这里才是实际给元素位置赋值的地方!!!!// 查询是否有对应的模块然后, 对应的赋值this.componentsList.forEach((item) => {if (item.identifier == this.identifier) {console.log(item, "找到了");item.temp.position.x = x;item.temp.position.y = y;item.position.x = x;item.position.y = y;}});this.flag = "";},// 拖拽元素handleDrapEvList(event, value) {let { offsetX, offsetY } = event;var infoJson = JSON.stringify({...value,position: {...value.position,x: offsetX,y: offsetY,},});//   将数据绑定到dataTransfer身上event.dataTransfer.setData("drapData", infoJson);},// 监听拖拽元素结束handleDrap(event) {event.preventDefault();const value = event.dataTransfer.getData("drapData");//   获取绑定到拖拽元素身上的 drapData属性if (value) {let drapData = JSON.parse(value);const { position } = drapData;const identifier = Math.floor(Math.random() * 10000);this.componentsList.push({...drapData,identifier,position: {...position,x: event.offsetX - position.x,y: event.offsetY - position.y,},temp: {position: {x: event.offsetX - position.x,y: event.offsetY - position.y,},},});}},// 点击元素获取组件配置handleClickTarget(row, index) {console.log(row);this.identifier = row.identifier;this.curControl = row;},// 移动元素handleMouseDown(e, row, index) {this.flag = "move";// 获取组件配置, 为接下来的属性配置做准备this.handleClickTarget(row, index);e = e || window.event;// 记录下当前点击的位置this.containerMoveObj.x = e.pageX;this.containerMoveObj.y = e.pageY;},},
};
</script><style lang="scss">
.box {display: flex;flex-direction: row;align-items: center;position: relative;height: 500px;.drap {width: 300px;height: 500px;background: #f2f2f2;display: flex;flex-direction: row;flex-wrap: wrap;cursor: pointer;.drap-item {height: 120px;margin-right: 20px;.drap-item-img {display: block;width: 80px;height: 80px;}.drap-item-name {text-align: center;}}}.drap-container {flex: 1;height: 500px;background: #ccc;position: relative;.drap-container-item {-webkit-user-select: none;-moz-user-select: none;-o-user-select: none;user-select: none;position: absolute;user-select: none;cursor: pointer;border: 1px solid transparent;.drap-item-img {display: block;width: 100%;// height: 80px;user-select: none;}.drap-item-name {text-align: center;}}.drap-container-item-active {border: 1px solid skyblue;}}
}
</style>

将组件加入首页展示

<template><nav><router-link to="/">Home</router-link>| <router-link to="/about">About</router-link>| <router-link to="/componentType">componentType</router-link>| <router-link to="/component">component</router-link>| <router-link to="/sort">sort</router-link>| <router-link to="/center">center</router-link></nav><router-view/>
</template>
<style lang="scss">
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;
}nav {padding: 30px;a {font-weight: bold;color: #2c3e50;&.router-link-exact-active {color: #42b983;}}
}
</style>

路由也需要添加

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import HomeView from '../views/HomeView.vue'const routes: Array<RouteRecordRaw> = [{path: '/',name: 'home',component: HomeView},{path: '/about',name: 'about',// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is visited.component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')},{path: '/componentType',name: 'componentType',// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is visited.component: () => import(/* webpackChunkName: "about" */ '../views/componentType/ComponentTypeIndex.vue')},{path: '/component',name: 'component',// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is visited.component: () => import(/* webpackChunkName: "about" */ '../views/component/ComponentIndex.vue')},{path: '/sort',name: 'sort',// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is visited.component: () => import(/* webpackChunkName: "about" */ '../views/sort/sort.vue')},{path: '/center',name: 'center',// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is visited.component: () => import(/* webpackChunkName: "about" */ '../views/center/center.vue')}
]const router = createRouter({history: createWebHistory(process.env.BASE_URL),routes
})export default router

可持续发展项目(九):前端页面实现相关推荐

  1. Struts项目中前端页面向后台页面传参中文出现乱码(Get请求)

    问题描述:Struts项目中前端页面向后台页面传递中文参数值,中文值传递到后台后出现乱码并且以???形式出现 解决方法: 1.前端页面js文件中使用encodeURI()方法将所传递的中文值加密起来( ...

  2. Web项目中前端页面引用外部Js和Css的路径问题

    公众号:南宫一梦 Web项目中前端页面引用外部Js和Css的路径问题 一般我们在做Web项目时,通常会将多个页面引入的公共js和css文件抽取出来,单独写成一个公共文件,以期方便各个页面单独引入,达到 ...

  3. SpringBoot个人博客项目搭建—前端页面功能介绍(一)

    SpringBoot个人博客-前端页面功能介绍(一) 项目首页地址:https://blog.csdn.net/weixin_45019350/article/details/108869025 一. ...

  4. Web项目中前端页面通过URL传中文或 # 特殊字符到后台出现乱码解决方案

    对于刚入行新手来说碰到URL传递中文数据到后台出现乱码或是传递包含特使符号'#'后端获取不到的情况是比较头疼的事,这里我就告诉你这两个的解决的方式: 1.URL传递中文 前端页面:中文字符串用 enc ...

  5. 微信小程序--证件照换底色项目前端修改+头像框项目前端页面实现修改

    证件照换底色 主要进行后端所需功能的增加,尺寸选择功能的增加,颜色选择功能样式修改,版权问题导致的ui修改和所需ui的设计. 跳转前按钮判断 在点击开始转换图片进行跳转之前,判断用户是否已经进行授权, ...

  6. java前端项目经验_web前端页面项目经验总结

    项目时间:2016年4月5日--4月9日 项目名称:阿七果子园web前端页面 项目内容: 1.HTML5+CSS+JavaScript(banner+timer)+JQuery(small_banne ...

  7. php上传图片到非项目目录,前端页面的读取问题

    一.前言 关于上传文件部分的危险,一直以来都有听说,但是之前为了方便,一直都是直接放到项目根目录,方便访问.只是现在项目越来越大,安全问题虽然不用刻意追求,但这些基本的地方还是要注意一下的.上传的路径 ...

  8. SpringMVC(SSM)框架搭建JavaWeb项目时,前端页面文件上传,后台Java下载功能实现及相关问题记录说明

    看在前面:前端页面通过input控件实现文件上传,后台Java使用SpringMVC框架的实现网上有较多教程,但是真正配置一遍下来不报错的较少,所以本博客前面先介绍一遍完整的设置步骤,然后介绍遇到的一 ...

  9. 商城项目解析(前端页面知识,用户如何访问服务器,hosts的修改,nginx)

    接上一篇商城分析继续写(在我的博客里面),大家如果想要看之前的分析就去我的博客里面去找,我下面会提到一些之前博客里面写的模块. 我们还需要搭建一个模块,就是一个工具类,叫做common模块,这个模块具 ...

  10. 【HTML响应式项目】成人教育官网前端页面(HTML+CSS+JS实现三端适应)

    项目源码已上传至码云仓库:云南农业职业技术学院/HTML响应式成人教育官网前端页面(HTML+CSS+JS实现) 项目演示地址:成人教育网 AAP端下载地址:成人教育网APP端.apk-互联网文档类资 ...

最新文章

  1. 用JPUSH极光推送实现服务端向安装了APP应用的手机推送消息(C#服务端接口)
  2. 2021年夏季学期“清华大学大数据能力提升项目” 招募《大数据实践课》企业合作项目...
  3. nginx源码编译和集群及高可用
  4. 栈上对象的内存自动释放
  5. Vue实现仿音乐播放器4-Vue-router实现音乐导航菜单切换
  6. [BUUCTF-pwn]——jarvisoj_tell_me_something
  7. Flutter MaterialApp概述以及主题配置概述
  8. Google协作平台中文版BUG两条
  9. stl变易算法(一)
  10. JavaScript 函数参数是传值(byVal)还是传址(byRef)?
  11. MATLAB 多元多项式的除法
  12. 进销存设计之——进销存和财务软件的对接
  13. 全国大学生英语竞赛【常考词汇】
  14. 1699 个词汇 的 计算机英语
  15. iOS网络编程---根据URL下载网络文件的方法
  16. 数据结构之图的基础知识(二)
  17. GIS软件——arcgis10.2制作符号并链接符号库
  18. 2023年网络安全HW攻防技术总结(珍藏版)
  19. 滴滴当年重创的安全事件,也会重创货拉拉吗?
  20. h5 vr效果_H5案例|通过VR展示的那些烧脑游戏

热门文章

  1. Android Wear创建一个通知
  2. 合同比对案例之廊坊银行
  3. 基于星形胶质细胞神经网络的容错计算设计方法;忆阻器的三组分漂移扩散模型;基于脑电的脑机接口的生理信息数据扩充;神经网络在类人语音识别中的成功与关键失败
  4. echo java.ext.dirs_找寻gvcf失败的原因
  5. pandas基础学习教程笔记 (1)
  6. SpringCloud之Eureka配置翻译
  7. qc25 iphone android,亲耳听出降噪性能差:苹果 AirPods Max 和 Bose QC35 II 的对决
  8. ThreadingTest延用方法,打破结果,展现测试新理念
  9. [独有源码]java-jsp传统文化知识竞赛系统vyg09规划与实现适合自己的毕业设计的策略
  10. 向HDFS中指定的文件追加内容,由用户指定内容追加到原有文件的开头或结尾