One: [understand] needs/pain points
1. What is a drug instruction manual?
Instructions should include the content of the drug name, specifications, production enterprises, drug approval number, product batch number, validity period, the main ingredient, indications or functions indication, usage, dosage, taboos, adverse reactions and points for attention, instruction should also include the main ingredients of TCM (ingredients) character, pharmacological action and storage, etc.
For example: aspirin enteric-coated tablet instructions
2. What was the user’s original job?
Users need to manually copy and paste 30 fields such as [drug components] [traits] in the drug instructions into the System one by one to complete the input work.
3. What are the pain points of users?
If the user wants to enter a drug specification, they need to:
Step 1: Open two screens at the same time, or switch back and forth with the same screen.
Step 2: Find the corresponding field information of the manual – copy, switch to the “System” operation page, find the corresponding field, paste.
Step 3: Repeat step 2 N times.
Step 4: Commit to save.
Pain point: users have a lot of repetitive work, time-consuming, tedious.
4. What are the user needs?
Can make a small tool, automatically copy the contents of the manual, switch to the “system”, click the button, automatically parse the field information, match to the form, save the [second] [third] operation.
5. Specific implementation scheme
The goal: to find a solution that is most comfortable for users and balances the technical complexity and workload of development.
Option 1. Users manually select all the contents of the drug instructions, copy them, switch to “System”, click the button, and paste.
Easy to misoperate, not recommended.
Scheme 2,Save web pages by using Control + S (Command + S).This generates two “things” : an HTML file and a files folder (which holds CSS \js\ images, etc.)
In The System, the user uploads these two “things”, clicks the button, and starts automatic parsing, filling, and submitting to save.
The user said: Ok!
Final decision: Option 2.
6. In-depth understanding of user needs
Enter the website of drug instructions and randomly check the instructions of N drugs (albendazole tablets, Ornidazole sodium chloride injection, ornidazole sodium chloride injection……) , it was concluded that the data were consistent and regular, but the contents of some drug instructions did not conform to the rules.
The user wants to automatically parse and fill 30 fields: plain text data, extract key information in plain text, array data, rich text data, rich text supported images/tables, date data, etc.
Ii. [Benchmarking] User expectations
Because the data is not completely unified, the correctness of the data cannot be guaranteed 100%. Users need to check the data again before saving it.
Such as:
1. Rare words are replaced by pictures.The objects are pictures and cannot be copied into the input box.
2, the latest modification date (format is not completely unified, some in accordance with the newline segmentation, some in accordance with the space segmentation, some Chinese comma segmentation……)
3, specification approval number: “system” is a plurality of approval number, most of the drug specification is 1 approval number, there are few cases of multiple approval number, if more than one case, need to be manually handled by the user.
4. Drug specifications and data are irregular.
Single case: 0.25g(C10H3NO4) is rich text, rich text (C10H3NO4) is lost in the input box, if you don’t want this molecular text, you need to manually delete it.
Multiple cases :(1)50mg and (2)100mg are not displayed according to line feed. If no multiple segmentation rules are found, it is automatically pasted to an input box and needs to be manually processed by the user.
Three: [Optimization] use experience
Solution a:
1. Click Upload picture, and the user selects the picture file in the Files file to upload.
2. Select the HTML file and click Upload.
3. Automatic parsing.
4. User verification.
5. Submit and save.
Users said: the process is a bit cumbersome, can upload all at once?
The process is indeed redundant and allows the user to visually filter out image files (JPG/PNG/GIF/BMP…) Not very friendly indeed.
Scheme 2:
1. After saving the HTML file, move it to the files folder.
2. Click upload all files in the files folder.
3. Automatic parsing.
4. User verification.
5. Submit and save.
Got the product, the test, the user’s thumbs-up 👍, done!
Four: [technology] automatic parsing
1. File upload
1.1. Filter out image files and upload them to the server
// This. FileList is an array containing a list of files
onChangeFile (file, fileList) {
const length = fileList.length - this.fileList.length
if (length > 1) { // Overwrite the previous file list when the file is uploaded again
fileList.splice(0, fileList.length - 1)}this.fileList.push(file)
},
// Click the start parsing button
handleExplain () {
const item = this.fileList.find(ele= > ele.raw.type === 'text/html' && ele.raw.name.indexOf('Drug instructions')! = = -1)
if(! item)return this.$message.error('Please upload the manual file! ')
let promise = []
this.fileList.forEach(ele= > {
if (ele.raw.type.split('/') [0= = ='image') { // Filter out image files
promise.push(new Promise((resolve, reject) = > {
this.handleUploadImageFile(ele, resolve, reject) // Upload the image file function, after successful upload returns the image storage address.}}})))// After uploading all images successfully, process the HTML file. To replace the img SRC in the HTML file, make sure that all images are uploaded before parsing the HTML file. Otherwise, the image address cannot be obtained when replacing the IMG SRC and the image cannot be displayed.
Promise.allSettled(promise).then(() = > {
this.handleHtmlFile(item.raw) // A function to parse HTML files})},// Upload the image file
handleUploadImageFile (file, resolve, reject) {
const myForm = new FormData()
myForm.append('file', file.raw)
uploadImage(myForm).then(({ data }) = > {
resolve(data)
// {'111.jpg': 'https://imgurl.com'} Map between image name and image address
this.copyImageUrl[file.raw.name] = data.originImageURL
}).catch(() = >{})},Copy the code
1.2. After uploading the image successfully, start parsing the HTML file
// Parse the HTML file
handleHtmlFile (file) {
const that = this
if (window.FileReader) {
const reader = new FileReader()
reader.onload = function () {
that.srcdoc = this.result / / the iframe srcdoc
const iframeCopy = document.getElementById('copyProduct')
iframeCopy.onload = function () {
const iframeDocument = iframeCopy.contentWindow.document // Get the document object of the iframe
const elements = iframeDocument.getElementsByClassName('content_class')
for (let i = 0; i < elements.length; i++) {
const item = elements[i]
// Processing date (pseudocode)
if ('Specification date') {
that.handleDate(item)
}
// Handle plain text data (pseudocode)
if ('Plain text data') {
that.handleCopyText(item)
}
// Handle array format data (pseudocode)
if ('Array format data') {
that.handleCopyArray(item)
}
// Extract the "manufacturer" keyword (pseudocode)
if ('Production enterprise') {
that.handleManufacturerName(item)
}
// Handle rich text (pseudocode)
if ('Rich text') {
that.handleCopyRichText(item)
}
}
}
}
reader.readAsText(file)
} else {
this.$message({
message: 'Please use Chrome'.type: 'warning'})}},Copy the code
2. File parsing
2.1. Plain text
handleCopyText (content) {
while (content.nodeName === '#text') {
content = content.nextSibling
}
let text = content.innerText.replace(/\s+/g.' ').replace(/[\r\n]/g.' ')
// Insert text into system
},
Copy the code
2.2. Key data are presented in plain text
// Revision date - reviseDate
let reviseDateList = content.innerHTML.match(/ Modified date:.+/g)
if (reviseDateList) {
let reviseDateStr = reviseDateList[0]
let reviseDateArray = reviseDateStr.match(/[0-9]{4} year [0-9]{1,2} month [0-9]{1,2} day /g)
// Use regular expressions to extract date data
}
Copy the code
2.3. Rich text plug-in
1. Image SRC regular replacement
Replace the SRC of the img in the HTML file with the address of the uploaded image.
Img SRC for the replaced: < img SRC = “https://imgurl/110379470.jpg” >
Because the replacement is necessary to have the network address after uploading the picture, echoing the above:
Promise.allSettled(promise).then(() => { this.handleHtmlFile(item.raw) })
Only after all images have been uploaded to allSettled can the HTML file be parsed and replaced with img SRC.
// Handle image replacement
if (content.innerHTML.indexOf('<img')! = = -1) {
content.innerHTML = content.innerHTML.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/g.(match) = > {
let str1 = ' '
Object.keys(this.copyImageUrl).forEach(ele= > {
if(match.indexOf(ele) ! = = -1) {
str1 = '<img src=' + this.copyImageUrl[ele] + '>'}})return str1
})
}
Copy the code
2. The original rich text plugin does not support tables
Users emphasized that rich text should support images and tables. Since the original rich text plug-in does not support tables, this issue needs to be resolved first.
The original rich text plugin is Quill. The latest version 1.3.7 does not support tables. Tables will only be supported if you upgrade to version 2.0, but only the Dev branch, not the Release branch.
3. Upgrade the original rich text plugin to support tables
Replace the rich text plug-in, to be compatible with old data, test sister to return to the whole rich text function, feel sorry for our professional/responsible/serious/beautiful/gentle test sister, first upgrade the original rich text plug-in to try.
// Upgrade quill to version 2.0
npm i quill@2.0. 0-dev4.
// Install the table plug-in
npm i quill-better-table
// Create a table based on the document
import QuillBetterTable from 'quill-better-table'
Quill.register({
'modules/better-table': QuillBetterTable
}, true)
// Generate an editor instance after the page mounts
const quill = new Quill('#editor-wrapper', {
theme: 'snow'.modules: {
table: false.'better-table': {
operationMenu: {
items: {
unmergeCells: {
text: 'Another unmerge cells name'}}}},keyboard: {
bindings: QuillBetterTable.keyboardBindings
}
}
})
document.body.querySelector('#insert-table')
.onclick = () = > {
let tableModule = quill.getModule('better-table')
tableModule.insertTable(3.3)}Copy the code
After upgrading Quill, check old data compatibility and table support
- Older data compatibility: version 2.0 does not support version 1.3.7
<pre></pre>
The original Pre tag cannot be displayed. 👎 - Table support: The custom data-row attribute of a table is bound to the row and cell relationship. A simple table table (without the custom data-row attribute binding) cannot be displayed. 👎
Quill, quill, quill, quill, quill!
4. Replace the rich text plug-in
Replacement for wangeditor
/ / wangeditor installation
npm i wangeditor
Copy the code
<template lang="html">
<div class="editor">
<div ref="toolbar" class="toolbar"></div>
<div ref="editor" class="text"></div>
</div>
</template>
<script>
import Editor from 'wangeditor'
import { uploadImage } from '@/api' // Upload image API
import EditorSup from '@/components/wangEditorSup.js' // Customize the superscript menu
import EditorSub from '@/components/wangEditorSub.js' // Customize the subscript menu
const menuKeySub = 'editorSub'
const menuKeySup = 'editorSup'
Editor.registerMenu(menuKeySub, EditorSub)
Editor.registerMenu(menuKeySup, EditorSup)
export default {
name: 'EditorItem',
data () {
return {
editor: null.info_: null}},model: {
prop: 'value'.event: 'change'
},
props: {
value: {
type: String.default: ' '
},
isClear: {
type: Boolean.default: false}},watch: {
isClear (val) { // Trigger to clear text field content
if (val) {
this.editor.txt.clear()
this.info_ = null}},value: function (value) {
if(value ! = =this.editor.txt.html()) {
this.editor.txt.html(this.value)
}
}
},
mounted () {
this.setEditor()
this.editor.txt.html(this.value)
},
methods: {
// Verify rich-text images before uploading
beforeAvatarUpload (file) {
const isImage = file.type.indexOf('image')! = = -1
const isLtSize = file.size / 1024 / 1024 < 19
if(! isImage) {this.$message.error(` 【${file.name}Failed to upload, can only upload pictures! `)}if(! isLtSize) {this.$message.error(` 【${file.name}Upload failed, image size cannot exceed 19MB! `)}return isImage && isLtSize
},
// Rich text initialization
setEditor () {
this.editor = new Editor(this.$refs.toolbar, this.$refs.editor)
// Set the server interface address
this.editor.config.uploadImgMaxSize = 200 * 1024 * 1024 // There is no limit here
this.editor.config.uploadImgAccept = [] // There is no restriction here
this.editor.config.uploadImgServer = '/url'
// Customize image upload
this.editor.config.customUploadImg = (resultFiles, insertImgFn) = > {
const file = resultFiles[0]
if (!this.beforeAvatarUpload(file)) return
const myForm = new FormData()
myForm.append('file', file)
uploadImage(myForm).then(({ data }) = > {
const imgUrl = data.originImageURL
insertImgFn(imgUrl) // Insert the image into a rich text editor
}).catch(() = >{})}// Configuration menu
this.editor.config.menus = [
'bold'.// 1
'italic'.// 2
'underline'.// 3
'strikeThrough'.// 4, delete line
'quote'.// 5
'code'.// 6, insert code
'head'.// 7
'list'.// 8
'indent'.// 9, indent
'fontSize'.// 10
'fontName'.// 11
'foreColor'.// text color
'backColor'.// background color
'justify'.// 14
'link'.// 15, insert link
'image'.// 16, insert the image
'video'.// insert the video
'table' // 18
]
this.editor.config.onchange = (html) = > {
this.info_ = html // Bind the current value
this.$emit('change'.this.info_) // Synchronize the content to the parent component
}
// Create a rich text editor
this.editor.create()
}
}
}
</script>
<style lang="css">
.editor {
width: 900px;
margin: 0 auto;
position: relative;
z-index: 0;
}
.toolbar {
border: 1px solid #ccc;
}
.text {
height: 300px;
border: 1px solid #ccc;
overflow: auto;
}
</style>
Copy the code
Since the drug instructions contain the drug molecular formula (e.g. CH4), we rely on the “superscript” and “subscript” menus, so we customize the “superscript” and “subscript” menus.
Subscript component (wangEditorsub.js) :
import E from 'wangeditor'
const { BtnMenu, DropListMenu, PanelMenu, DropList, Panel, Tooltip } = E
class EditorSub extends BtnMenu {
constructor (editor) {
const $elem = E.$(
`
)
`super($elem, editor)
this.editor = editor
}
// Menu click events
clickHandler (value) {
this.editor.cmd.do('insertHTML'.'<span>T<sub>x</sub></span>')}// Menu active status
tryChangeActive () {
this.active() // Menu activation
// this.unactive () // The menu is not active}}export default EditorSub
Copy the code
Superscript component (wangEditorsub.js) :
import E from 'wangeditor'
const { BtnMenu, DropListMenu, PanelMenu, DropList, Panel, Tooltip } = E
class EditorSup extends BtnMenu {
constructor (editor) {
const $elem = E.$(
`
)
`super($elem, editor)
this.editor = editor
}
// Menu click events
clickHandler (value) {
this.editor.cmd.do('insertHTML'.'<span>T<sup>x</sup></span>')}// Menu active status
tryChangeActive () {
this.active() // Menu activation
// this.unactive () // The menu is not active}}export default EditorSup
Copy the code
After changing to WangEditor, check old data compatibility and table support:
- Old data compatibility: original rich text data, all display normal (slightly different style). 👍
- Table support: Simple tables are supported. 👍
- Image: Supported. 👍
- Custom menu: superscript and subscript, supported. 👍
Replace it with the WangEditor plug-in, and you’re done!
Five: [self-test]
Automatic analysis series of drug instructions:
- Benzene piece o
- Ornidazole sodium chloride injection
- Citicoline sodium chloride injection
- Benzyl alcohol injection
- Rhinoceros fluid immersion tablet
- Metriprogesterone acetate dispersible tablet
- Lyophilized recombinant human interferon α 1B for eye drops — specifications are not very standard
- Glucose calcium chloride injection
- Olanzapine oral tablet
- Digoxin oral solution
- Bisoprolol fumarate capsule
- Noxinacan sodium injection
- Bicarbonate tablet
- Nitroglycerin spray
Accuracy :(13/14)*100 = 92%.